Skip to content

Commit 44e1411

Browse files
ceyonurDarioush Jalali
andauthored
add standalone dbs and configs (#1354)
* add standalone dbs and configs * show value in config * fix tests * move to vm * remove the noSkip flag * reviews * check isMemDB earlier * rename var * return nit --------- Co-authored-by: Darioush Jalali <[email protected]>
1 parent de75e82 commit 44e1411

File tree

6 files changed

+209
-36
lines changed

6 files changed

+209
-36
lines changed

plugin/evm/config.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"fmt"
99
"time"
1010

11+
"github.com/ava-labs/avalanchego/database/pebbledb"
1112
"github.com/ava-labs/subnet-evm/core/txpool/legacypool"
1213
"github.com/ava-labs/subnet-evm/eth"
1314
"github.com/ethereum/go-ethereum/common"
@@ -60,8 +61,11 @@ const (
6061
// - state sync time: ~6 hrs.
6162
defaultStateSyncMinBlocks = 300_000
6263
defaultStateSyncRequestSize = 1024 // the number of key/values to ask peers for per request
64+
defaultDBType = pebbledb.Name
6365
)
6466

67+
type PBool bool
68+
6569
var (
6670
defaultEnabledAPIs = []string{
6771
"eth",
@@ -225,6 +229,14 @@ type Config struct {
225229

226230
// RPC settings
227231
HttpBodyLimit uint64 `json:"http-body-limit"`
232+
233+
// Database settings
234+
UseStandaloneDatabase *PBool `json:"use-standalone-database"`
235+
DatabaseConfigContent string `json:"database-config"`
236+
DatabaseConfigFile string `json:"database-config-file"`
237+
DatabaseType string `json:"database-type"`
238+
DatabasePath string `json:"database-path"`
239+
DatabaseReadOnly bool `json:"database-read-only"`
228240
}
229241

230242
// EthAPIs returns an array of strings representing the Eth APIs that should be enabled
@@ -284,6 +296,7 @@ func (c *Config) SetDefaults() {
284296
c.StateSyncRequestSize = defaultStateSyncRequestSize
285297
c.AllowUnprotectedTxHashes = defaultAllowUnprotectedTxHashes
286298
c.AcceptedCacheSize = defaultAcceptedCacheSize
299+
c.DatabaseType = defaultDBType
287300
}
288301

289302
func (d *Duration) UnmarshalJSON(data []byte) (err error) {
@@ -338,3 +351,17 @@ func (c *Config) Deprecate() string {
338351

339352
return msg
340353
}
354+
355+
func (p *PBool) String() string {
356+
if p == nil {
357+
return "nil"
358+
}
359+
return fmt.Sprintf("%t", *p)
360+
}
361+
362+
func (p *PBool) Bool() bool {
363+
if p == nil {
364+
return false
365+
}
366+
return bool(*p)
367+
}

plugin/evm/syncervm_test.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,9 +273,7 @@ func TestVMShutdownWhileSyncing(t *testing.T) {
273273
}
274274

275275
func createSyncServerAndClientVMs(t *testing.T, test syncTest, numBlocks int) *syncVMSetup {
276-
var (
277-
require = require.New(t)
278-
)
276+
require := require.New(t)
279277
// configure [serverVM]
280278
_, serverVM, _, serverAppSender := GenesisVM(t, true, genesisJSONLatest, "", "")
281279
t.Cleanup(func() {

plugin/evm/vm.go

Lines changed: 176 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package evm
55

66
import (
77
"context"
8+
"encoding/base64"
89
"encoding/json"
910
"errors"
1011
"fmt"
@@ -20,6 +21,7 @@ import (
2021
"github.com/ava-labs/avalanchego/network/p2p/gossip"
2122
"github.com/prometheus/client_golang/prometheus"
2223

24+
avalancheNode "github.com/ava-labs/avalanchego/node"
2325
"github.com/ava-labs/subnet-evm/commontype"
2426
"github.com/ava-labs/subnet-evm/consensus/dummy"
2527
"github.com/ava-labs/subnet-evm/constants"
@@ -66,7 +68,10 @@ import (
6668
avalancheRPC "github.com/gorilla/rpc/v2"
6769

6870
"github.com/ava-labs/avalanchego/codec"
69-
"github.com/ava-labs/avalanchego/database"
71+
"github.com/ava-labs/avalanchego/database/leveldb"
72+
"github.com/ava-labs/avalanchego/database/memdb"
73+
"github.com/ava-labs/avalanchego/database/meterdb"
74+
"github.com/ava-labs/avalanchego/database/pebbledb"
7075
"github.com/ava-labs/avalanchego/database/prefixdb"
7176
"github.com/ava-labs/avalanchego/database/versiondb"
7277
"github.com/ava-labs/avalanchego/ids"
@@ -81,7 +86,10 @@ import (
8186

8287
commonEng "github.com/ava-labs/avalanchego/snow/engine/common"
8388

89+
avalanchemetrics "github.com/ava-labs/avalanchego/api/metrics"
90+
"github.com/ava-labs/avalanchego/database"
8491
avalancheUtils "github.com/ava-labs/avalanchego/utils"
92+
avalancheconstants "github.com/ava-labs/avalanchego/utils/constants"
8593
avalancheJSON "github.com/ava-labs/avalanchego/utils/json"
8694
)
8795

@@ -106,6 +114,7 @@ const (
106114
ethMetricsPrefix = "eth"
107115
sdkMetricsPrefix = "sdk"
108116
chainStateMetricsPrefix = "chain_state"
117+
dbMetricsPrefix = "db"
109118

110119
// gossip constants
111120
pushGossipDiscardedElements = 16_384
@@ -201,7 +210,6 @@ type VM struct {
201210
// [acceptedBlockDB] is the database to store the last accepted
202211
// block.
203212
acceptedBlockDB database.Database
204-
205213
// [warpDB] is used to store warp message signatures
206214
// set to a prefixDB with the prefix [warpPrefix]
207215
warpDB database.Database
@@ -246,6 +254,7 @@ type VM struct {
246254
ethTxPushGossiper avalancheUtils.Atomic[*gossip.PushGossiper[*GossipEthTx]]
247255
ethTxPullGossiper gossip.Gossiper
248256

257+
chainAlias string
249258
// RPC handlers (should be stopped before closing chaindb)
250259
rpcHandlers []interface{ Stop() }
251260
}
@@ -284,8 +293,9 @@ func (vm *VM) Initialize(
284293
// fallback to ChainID string instead of erroring
285294
alias = vm.ctx.ChainID.String()
286295
}
296+
vm.chainAlias = alias
287297

288-
subnetEVMLogger, err := InitLogger(alias, vm.config.LogLevel, vm.config.LogJSONFormat, vm.ctx.Log)
298+
subnetEVMLogger, err := InitLogger(vm.chainAlias, vm.config.LogLevel, vm.config.LogJSONFormat, vm.ctx.Log)
289299
if err != nil {
290300
return fmt.Errorf("failed to initialize logger due to: %w ", err)
291301
}
@@ -306,16 +316,15 @@ func (vm *VM) Initialize(
306316

307317
vm.toEngine = toEngine
308318
vm.shutdownChan = make(chan struct{}, 1)
309-
// Use NewNested rather than New so that the structure of the database
310-
// remains the same regardless of the provided baseDB type.
311-
vm.chaindb = rawdb.NewDatabase(Database{prefixdb.NewNested(ethDBPrefix, db)})
312-
vm.db = versiondb.New(db)
313-
vm.acceptedBlockDB = prefixdb.New(acceptedPrefix, vm.db)
314-
vm.metadataDB = prefixdb.New(metadataPrefix, vm.db)
315-
// Note warpDB is not part of versiondb because it is not necessary
316-
// that warp signatures are committed to the database atomically with
317-
// the last accepted block.
318-
vm.warpDB = prefixdb.New(warpPrefix, db)
319+
320+
if err := vm.initializeMetrics(); err != nil {
321+
return fmt.Errorf("failed to initialize metrics: %w", err)
322+
}
323+
324+
// Initialize the database
325+
if err := vm.initializeDBs(db); err != nil {
326+
return fmt.Errorf("failed to initialize databases: %w", err)
327+
}
319328

320329
if vm.config.InspectDatabase {
321330
start := time.Now()
@@ -466,10 +475,6 @@ func (vm *VM) Initialize(
466475
}
467476
log.Info(fmt.Sprintf("lastAccepted = %s", lastAcceptedHash))
468477

469-
if err := vm.initializeMetrics(); err != nil {
470-
return err
471-
}
472-
473478
// initialize peer network
474479
if vm.p2pSender == nil {
475480
vm.p2pSender = appSender
@@ -800,8 +805,8 @@ func (vm *VM) initBlockBuilding() error {
800805
// setAppRequestHandlers sets the request handlers for the VM to serve state sync
801806
// requests.
802807
func (vm *VM) setAppRequestHandlers() {
803-
// Create separate EVM TrieDB (read only) for serving leafs requests.
804-
// We create a separate TrieDB here, so that it has a separate cache from the one
808+
// Create standalone EVM TrieDB (read only) for serving leafs requests.
809+
// We create a standalone TrieDB here, so that it has a standalone cache from the one
805810
// used by the node when processing blocks.
806811
evmTrieDB := triedb.NewDatabase(
807812
vm.chaindb,
@@ -1010,13 +1015,9 @@ func (vm *VM) CreateHandlers(context.Context) (map[string]http.Handler, error) {
10101015
return nil, err
10111016
}
10121017

1013-
primaryAlias, err := vm.ctx.BCLookup.PrimaryAlias(vm.ctx.ChainID)
1014-
if err != nil {
1015-
return nil, fmt.Errorf("failed to get primary alias for chain due to %w", err)
1016-
}
10171018
apis := make(map[string]http.Handler)
10181019
if vm.config.AdminAPIEnabled {
1019-
adminAPI, err := newHandler("admin", NewAdminService(vm, os.ExpandEnv(fmt.Sprintf("%s_subnet_evm_performance_%s", vm.config.AdminAPIDir, primaryAlias))))
1020+
adminAPI, err := newHandler("admin", NewAdminService(vm, os.ExpandEnv(fmt.Sprintf("%s_subnet_evm_performance_%s", vm.config.AdminAPIDir, vm.chainAlias))))
10201021
if err != nil {
10211022
return nil, fmt.Errorf("failed to register service for admin API due to %w", err)
10221023
}
@@ -1190,3 +1191,154 @@ func attachEthService(handler *rpc.Server, apis []rpc.API, names []string) error
11901191

11911192
return nil
11921193
}
1194+
1195+
// useStandaloneDatabase returns true if the chain can and should use a standalone database
1196+
// other than given by [db] in Initialize()
1197+
func (vm *VM) useStandaloneDatabase(acceptedDB database.Database) (bool, error) {
1198+
// no config provided, use default
1199+
standaloneDBFlag := vm.config.UseStandaloneDatabase
1200+
if standaloneDBFlag != nil {
1201+
return standaloneDBFlag.Bool(), nil
1202+
}
1203+
1204+
// check if the chain can use a standalone database
1205+
_, err := acceptedDB.Get(lastAcceptedKey)
1206+
if err == database.ErrNotFound {
1207+
// If there is nothing in the database, we can use the standalone database
1208+
return true, nil
1209+
}
1210+
return false, err
1211+
}
1212+
1213+
// getDatabaseConfig returns the database configuration for the chain
1214+
// to be used by separate, standalone database.
1215+
func getDatabaseConfig(config Config, chainDataDir string) (avalancheNode.DatabaseConfig, error) {
1216+
var (
1217+
configBytes []byte
1218+
err error
1219+
)
1220+
if len(config.DatabaseConfigContent) != 0 {
1221+
dbConfigContent := config.DatabaseConfigContent
1222+
configBytes, err = base64.StdEncoding.DecodeString(dbConfigContent)
1223+
if err != nil {
1224+
return avalancheNode.DatabaseConfig{}, fmt.Errorf("unable to decode base64 content: %w", err)
1225+
}
1226+
} else if len(config.DatabaseConfigFile) != 0 {
1227+
configPath := config.DatabaseConfigFile
1228+
configBytes, err = os.ReadFile(configPath)
1229+
if err != nil {
1230+
return avalancheNode.DatabaseConfig{}, err
1231+
}
1232+
}
1233+
1234+
dbPath := filepath.Join(chainDataDir, "db")
1235+
if len(config.DatabasePath) != 0 {
1236+
dbPath = config.DatabasePath
1237+
}
1238+
1239+
return avalancheNode.DatabaseConfig{
1240+
Name: config.DatabaseType,
1241+
ReadOnly: config.DatabaseReadOnly,
1242+
Path: dbPath,
1243+
Config: configBytes,
1244+
}, nil
1245+
}
1246+
1247+
// initializeDBs initializes the databases used by the VM.
1248+
// If [useStandaloneDB] is true, the chain will use a standalone database for its state.
1249+
// Otherwise, the chain will use the provided [avaDB] for its state.
1250+
func (vm *VM) initializeDBs(avaDB database.Database) error {
1251+
db := avaDB
1252+
// skip standalone database initialization if we are running in unit tests
1253+
if vm.ctx.NetworkID != avalancheconstants.UnitTestID {
1254+
// first initialize the accepted block database to check if we need to use a standalone database
1255+
verDB := versiondb.New(avaDB)
1256+
acceptedDB := prefixdb.New(acceptedPrefix, verDB)
1257+
useStandAloneDB, err := vm.useStandaloneDatabase(acceptedDB)
1258+
if err != nil {
1259+
return err
1260+
}
1261+
if useStandAloneDB {
1262+
// If we are using a standalone database, we need to create a new database
1263+
// for the chain state.
1264+
dbConfig, err := getDatabaseConfig(vm.config, vm.ctx.ChainDataDir)
1265+
if err != nil {
1266+
return err
1267+
}
1268+
log.Info("Using standalone database for the chain state", "DatabaseConfig", dbConfig)
1269+
db, err = vm.createDatabase(dbConfig)
1270+
if err != nil {
1271+
return err
1272+
}
1273+
}
1274+
}
1275+
// Use NewNested rather than New so that the structure of the database
1276+
// remains the same regardless of the provided baseDB type.
1277+
vm.chaindb = rawdb.NewDatabase(Database{prefixdb.NewNested(ethDBPrefix, db)})
1278+
vm.db = versiondb.New(db)
1279+
vm.acceptedBlockDB = prefixdb.New(acceptedPrefix, db)
1280+
vm.metadataDB = prefixdb.New(metadataPrefix, db)
1281+
// Note warpDB is not part of versiondb because it is not necessary
1282+
// that warp signatures are committed to the database atomically with
1283+
// the last accepted block.
1284+
// [warpDB] is used to store warp message signatures
1285+
// set to a prefixDB with the prefix [warpPrefix]
1286+
vm.warpDB = prefixdb.New(warpPrefix, db)
1287+
return nil
1288+
}
1289+
1290+
// createDatabase returns a new database instance with the provided configuration
1291+
func (vm *VM) createDatabase(dbConfig avalancheNode.DatabaseConfig) (database.Database, error) {
1292+
dbRegisterer, err := avalanchemetrics.MakeAndRegister(
1293+
vm.ctx.Metrics,
1294+
dbMetricsPrefix,
1295+
)
1296+
if err != nil {
1297+
return nil, err
1298+
}
1299+
var db database.Database
1300+
// start the db
1301+
switch dbConfig.Name {
1302+
case leveldb.Name:
1303+
dbPath := filepath.Join(dbConfig.Path, leveldb.Name)
1304+
db, err = leveldb.New(dbPath, dbConfig.Config, vm.ctx.Log, dbRegisterer)
1305+
if err != nil {
1306+
return nil, fmt.Errorf("couldn't create %s at %s: %w", leveldb.Name, dbPath, err)
1307+
}
1308+
case memdb.Name:
1309+
db = memdb.New()
1310+
case pebbledb.Name:
1311+
dbPath := filepath.Join(dbConfig.Path, pebbledb.Name)
1312+
db, err = pebbledb.New(dbPath, dbConfig.Config, vm.ctx.Log, dbRegisterer)
1313+
if err != nil {
1314+
return nil, fmt.Errorf("couldn't create %s at %s: %w", pebbledb.Name, dbPath, err)
1315+
}
1316+
default:
1317+
return nil, fmt.Errorf(
1318+
"db-type was %q but should have been one of {%s, %s, %s}",
1319+
dbConfig.Name,
1320+
leveldb.Name,
1321+
memdb.Name,
1322+
pebbledb.Name,
1323+
)
1324+
}
1325+
1326+
if dbConfig.ReadOnly && dbConfig.Name != memdb.Name {
1327+
db = versiondb.New(db)
1328+
}
1329+
1330+
meterDBReg, err := avalanchemetrics.MakeAndRegister(
1331+
vm.ctx.Metrics,
1332+
"meterdb",
1333+
)
1334+
if err != nil {
1335+
return nil, err
1336+
}
1337+
1338+
db, err = meterdb.New(meterDBReg, db)
1339+
if err != nil {
1340+
return nil, fmt.Errorf("failed to create meterdb: %w", err)
1341+
}
1342+
1343+
return db, nil
1344+
}

plugin/evm/vm_test.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -189,10 +189,8 @@ func setupGenesis(
189189
genesisBytes := buildGenesisTest(t, genesisJSON)
190190
ctx := NewContext()
191191

192-
baseDB := memdb.New()
193-
194192
// initialize the atomic memory
195-
atomicMemory := atomic.NewMemory(prefixdb.New([]byte{0}, baseDB))
193+
atomicMemory := atomic.NewMemory(prefixdb.New([]byte{0}, memdb.New()))
196194
ctx.SharedMemory = atomicMemory.NewSharedMemory(ctx.ChainID)
197195

198196
// NB: this lock is intentionally left locked when this function returns.
@@ -206,8 +204,7 @@ func setupGenesis(
206204
ctx.Keystore = userKeystore.NewBlockchainKeyStore(ctx.ChainID)
207205

208206
issuer := make(chan commonEng.Message, 1)
209-
prefixedDB := prefixdb.New([]byte{1}, baseDB)
210-
return ctx, prefixedDB, genesisBytes, issuer, atomicMemory
207+
return ctx, memdb.New(), genesisBytes, issuer, atomicMemory
211208
}
212209

213210
// GenesisVM creates a VM instance with the genesis test bytes and returns

plugin/evm/vm_upgrade_bytes_test.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,7 @@ import (
3333
"github.com/stretchr/testify/require"
3434
)
3535

36-
var (
37-
DefaultEtnaTime = uint64(upgrade.GetConfig(testNetworkID).EtnaTime.Unix())
38-
)
36+
var DefaultEtnaTime = uint64(upgrade.GetConfig(testNetworkID).EtnaTime.Unix())
3937

4038
func TestVMUpgradeBytesPrecompile(t *testing.T) {
4139
// Make a TxAllowListConfig upgrade at genesis and convert it to JSON to apply as upgradeBytes.

0 commit comments

Comments
 (0)