-
-
Save thehowl/cb1ee79e63cf77d3f323730580eb2d18 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/gno.land/pkg/gnoland/node_inmemory.go b/gno.land/pkg/gnoland/node_inmemory.go | |
index b7fe1161..ca75511a 100644 | |
--- a/gno.land/pkg/gnoland/node_inmemory.go | |
+++ b/gno.land/pkg/gnoland/node_inmemory.go | |
@@ -26,13 +26,22 @@ type InMemoryNodeConfig struct { | |
TMConfig *tmcfg.Config | |
GenesisTxHandler GenesisTxHandler | |
GenesisMaxVMCycles int64 | |
+ | |
+ // If CloneDB is set, it is used to re-create databases quickly. | |
+ // When MemDB.Size() == 0, it is used to call NewAppWithOptions, so the | |
+ // initialization can be saved; otherwise it is used to clone a new | |
+ // database. | |
+ // This allows skipping standard library initialization, speeding up tests. | |
+ CloneDB *memdb.MemDB | |
} | |
-// NewMockedPrivValidator generate a new key | |
+// NewMockedPrivValidator generate a new key. This is always the same after the first time it's called. | |
func NewMockedPrivValidator() bft.PrivValidator { | |
- return bft.NewMockPVWithParams(ed25519.GenPrivKey(), false, false) | |
+ return bft.NewMockPVWithParams(mockedPrivKey(), false, false) | |
} | |
+var mockedPrivKey = sync.OnceValue(ed25519.GenPrivKey) | |
+ | |
// NewInMemoryNodeConfig creates a default configuration for an in-memory node. | |
func NewDefaultGenesisConfig(pk crypto.PubKey, chainid string) *bft.GenesisDoc { | |
return &bft.GenesisDoc{ | |
@@ -87,18 +96,31 @@ func NewInMemoryNode(logger *slog.Logger, cfg *InMemoryNodeConfig) (*node.Node, | |
return nil, fmt.Errorf("validate config error: %w", err) | |
} | |
+ var appDB *memdb.MemDB | |
+ switch { | |
+ case cfg.CloneDB == nil || cfg.CloneDB.Size() == 0: | |
+ appDB = memdb.NewMemDB() | |
+ default: | |
+ appDB = cfg.CloneDB.Copy() | |
+ } | |
+ | |
// Initialize the application with the provided options | |
gnoApp, err := NewAppWithOptions(&AppOptions{ | |
Logger: logger, | |
GnoRootDir: cfg.TMConfig.RootDir, | |
GenesisTxHandler: cfg.GenesisTxHandler, | |
MaxCycles: cfg.GenesisMaxVMCycles, | |
- DB: memdb.NewMemDB(), | |
+ DB: appDB, | |
}) | |
if err != nil { | |
return nil, fmt.Errorf("error initializing new app: %w", err) | |
} | |
+ if cfg.CloneDB != nil && cfg.CloneDB.Size() == 0 { | |
+ cp := appDB.Copy() | |
+ *cfg.CloneDB = *cp | |
+ } | |
+ | |
cfg.TMConfig.LocalApp = gnoApp | |
// Setup app client creator | |
@@ -116,7 +138,7 @@ func NewInMemoryNode(logger *slog.Logger, cfg *InMemoryNodeConfig) (*node.Node, | |
// generate p2p node identity | |
// XXX: do we need to configur | |
- nodekey := &p2p.NodeKey{PrivKey: ed25519.GenPrivKey()} | |
+ nodekey := &p2p.NodeKey{PrivKey: mockedPrivKey()} | |
// Create and return the in-memory node instance | |
return node.NewNode(cfg.TMConfig, | |
diff --git a/gno.land/pkg/integration/testing_integration.go b/gno.land/pkg/integration/testing_integration.go | |
index 654dda0b..99acdeb6 100644 | |
--- a/gno.land/pkg/integration/testing_integration.go | |
+++ b/gno.land/pkg/integration/testing_integration.go | |
@@ -24,6 +24,7 @@ import ( | |
"github.com/gnolang/gno/tm2/pkg/crypto/bip39" | |
"github.com/gnolang/gno/tm2/pkg/crypto/keys" | |
"github.com/gnolang/gno/tm2/pkg/crypto/keys/client" | |
+ "github.com/gnolang/gno/tm2/pkg/db/memdb" | |
tm2Log "github.com/gnolang/gno/tm2/pkg/log" | |
"github.com/gnolang/gno/tm2/pkg/std" | |
"github.com/rogpeppe/go-internal/testscript" | |
@@ -88,6 +89,9 @@ func setupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { | |
// Testscripts run concurrently by default, so we need to be prepared for that. | |
nodes := map[string]*testNode{} | |
+ // used to speed up DB initialization for stdlibs | |
+ cloneDB := memdb.NewMemDB() | |
+ | |
updateScripts, _ := strconv.ParseBool(os.Getenv("UPDATE_SCRIPTS")) | |
persistWorkDir, _ := strconv.ParseBool(os.Getenv("TESTWORK")) | |
return testscript.Params{ | |
@@ -187,6 +191,7 @@ func setupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { | |
// setup genesis state | |
cfg.Genesis.AppState = *genesis | |
+ cfg.CloneDB = cloneDB | |
n, remoteAddr := TestingInMemoryNode(t, logger, cfg) | |
diff --git a/tm2/pkg/db/memdb/mem_db.go b/tm2/pkg/db/memdb/mem_db.go | |
index 09b90b6b..45c133b2 100644 | |
--- a/tm2/pkg/db/memdb/mem_db.go | |
+++ b/tm2/pkg/db/memdb/mem_db.go | |
@@ -8,6 +8,7 @@ import ( | |
"github.com/gnolang/gno/tm2/pkg/colors" | |
dbm "github.com/gnolang/gno/tm2/pkg/db" | |
"github.com/gnolang/gno/tm2/pkg/db/internal" | |
+ "golang.org/x/exp/maps" | |
) | |
func init() { | |
@@ -141,7 +142,7 @@ func (db *MemDB) Stats() map[string]string { | |
stats := make(map[string]string) | |
stats["database.type"] = "memDB" | |
- stats["database.size"] = fmt.Sprintf("%d", len(db.db)) | |
+ stats["database.size"] = fmt.Sprintf("%d", db.Size()) | |
return stats | |
} | |
@@ -196,3 +197,24 @@ func (db *MemDB) getSortedKeys(start, end []byte, reverse bool) []string { | |
} | |
return keys | |
} | |
+ | |
+// Copy creates a copy of db, independent from the original. | |
+func (db *MemDB) Copy() *MemDB { | |
+ db.mtx.Lock() | |
+ defer db.mtx.Unlock() | |
+ cp := make(map[string][]byte, len(db.db)) | |
+ for k, v := range db.db { | |
+ cp[k] = append([]byte{}, v...) | |
+ } | |
+ fmt.Println("COPY KEYS", maps.Keys(cp)) | |
+ return &MemDB{ | |
+ // we can avoid deep-copying the []byte, as the DB contract is that | |
+ // the user does not modify the byte slices. | |
+ db: cp, | |
+ } | |
+} | |
+ | |
+// Size returns the number of keys in the database. | |
+func (db *MemDB) Size() uint64 { | |
+ return uint64(len(db.db)) | |
+} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment