Skip to content

Instantly share code, notes, and snippets.

@thehowl

thehowl/a.diff Secret

Created July 4, 2024 15:07
Show Gist options
  • Save thehowl/cb1ee79e63cf77d3f323730580eb2d18 to your computer and use it in GitHub Desktop.
Save thehowl/cb1ee79e63cf77d3f323730580eb2d18 to your computer and use it in GitHub Desktop.
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