@@ -5,6 +5,7 @@ package evm
55
66import (
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.
802807func (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+ }
0 commit comments