From f01ad7695258b6e7593cc8a25a02ae1426608c03 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Tue, 10 Oct 2023 20:32:08 +0200 Subject: [PATCH 01/38] wip: gnoland inmemory Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/pkg/gnoland/app.go | 55 ++++- gno.land/pkg/gnoland/inmemory.go | 326 ++++++++++++++++++++++++++ gno.land/pkg/gnoland/inmemory_test.go | 16 ++ gno.land/pkg/gnoland/types.go | 22 ++ 4 files changed, 410 insertions(+), 9 deletions(-) create mode 100644 gno.land/pkg/gnoland/inmemory.go create mode 100644 gno.land/pkg/gnoland/inmemory_test.go diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index 3585f99d7de..fab57472fcd 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -1,11 +1,14 @@ package gnoland import ( + "errors" "fmt" "os" "os/exec" "path/filepath" + "runtime" "strings" + "sync" "github.com/gnolang/gno/gno.land/pkg/sdk/vm" "github.com/gnolang/gno/tm2/pkg/amino" @@ -36,7 +39,7 @@ func NewAppOptions() *AppOptions { return &AppOptions{ Logger: log.NewNopLogger(), DB: dbm.NewMemDB(), - GnoRootDir: GuessGnoRootDir(), + GnoRootDir: MustGuessGnoRootDir(), } } @@ -73,6 +76,8 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { // Construct keepers. acctKpr := auth.NewAccountKeeper(mainKey, ProtoGnoAccount) bankKpr := bank.NewBankKeeper(acctKpr) + + // XXX: Embed this ? stdlibsDir := filepath.Join(cfg.GnoRootDir, "gnovm", "stdlibs") vmKpr := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, stdlibsDir, cfg.MaxCycles) @@ -195,24 +200,56 @@ func EndBlocker(vmk vm.VMKeeperI) func(ctx sdk.Context, req abci.RequestEndBlock } } -func GuessGnoRootDir() string { - var rootdir string +var ( + guessOnce sync.Once + gnoroot string +) + +func MustGuessGnoRootDir() string { + root, err := guessGnoRootDir() + if err != nil { + panic(err) + } + + return root +} + +func GuessGnoRootDir() (string, error) { + var err error + guessOnce.Do(func() { + gnoroot, err = guessGnoRootDir() + }) + + return gnoroot, err +} +func guessGnoRootDir() (string, error) { // First try to get the root directory from the GNOROOT environment variable. - if rootdir = os.Getenv("GNOROOT"); rootdir != "" { - return filepath.Clean(rootdir) + if rootdir := os.Getenv("GNOROOT"); rootdir != "" { + return filepath.Clean(rootdir), nil } + // Try to guess GNOROOT using the nearest go.mod. if gobin, err := exec.LookPath("go"); err == nil { // If GNOROOT is not set, try to guess the root directory using the `go list` command. cmd := exec.Command(gobin, "list", "-m", "-mod=mod", "-f", "{{.Dir}}", "github.com/gnolang/gno") out, err := cmd.CombinedOutput() - if err != nil { - panic(fmt.Errorf("invalid gno directory %q: %w", rootdir, err)) + if err == nil { + return strings.TrimSpace(string(out)), nil } + } - return strings.TrimSpace(string(out)) + // Try to guess GNOROOT using caller stack. + if _, filename, _, ok := runtime.Caller(1); ok && filepath.IsAbs(filename) { + if currentDir := filepath.Dir(filename); currentDir != "" { + // Gno root directory relative from `app.go` path: + // gno/ .. /gno.land/ .. /pkg/ .. /gnoland/app.go + rootdir, err := filepath.Abs(filepath.Join(currentDir, "..", "..", "..")) + if err == nil { + return rootdir, nil + } + } } - panic("no go binary available, unable to determine gno root-dir path") + return "", errors.New("unable to guess gno's root-directory") } diff --git a/gno.land/pkg/gnoland/inmemory.go b/gno.land/pkg/gnoland/inmemory.go new file mode 100644 index 00000000000..d1971ad0c8b --- /dev/null +++ b/gno.land/pkg/gnoland/inmemory.go @@ -0,0 +1,326 @@ +package gnoland + +import ( + "errors" + "fmt" + "time" + + vmm "github.com/gnolang/gno/gno.land/pkg/sdk/vm" + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/gnovm/pkg/gnomod" + abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" + "github.com/gnolang/gno/tm2/pkg/bft/config" + "github.com/gnolang/gno/tm2/pkg/bft/node" + "github.com/gnolang/gno/tm2/pkg/bft/proxy" + "github.com/gnolang/gno/tm2/pkg/bft/types" + bft "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/crypto/ed25519" + "github.com/gnolang/gno/tm2/pkg/db" + "github.com/gnolang/gno/tm2/pkg/log" + "github.com/gnolang/gno/tm2/pkg/p2p" + "github.com/gnolang/gno/tm2/pkg/std" +) + +type InMemoryConfig struct { + RootDir string + ConsensusParams abci.ConsensusParams + GenesisValidator []bft.GenesisValidator + Packages []PackagePath + Balances []Balance + GenesisTXs []std.Tx + SkipFailingGenesisTxs bool + GenesisMaxVMCycles int64 +} + +func (im *InMemoryConfig) loadPackages() ([]std.Tx, error) { + txs := []std.Tx{} + for _, pkg := range im.Packages { + tx, err := pkg.load() + if err != nil { + return nil, fmt.Errorf("unable to load packages: %w", err) + } + txs = append(txs, tx...) + + } + return txs, nil +} + +func NewInMemory(logger log.Logger, icfg InMemoryConfig) (*node.Node, error) { + if icfg.RootDir == "" { + // XXX: Should return an error here ? + icfg.RootDir = MustGuessGnoRootDir() + } + + // Setup testing config + cfg := config.TestConfig().SetRootDir(icfg.RootDir) + { + cfg.EnsureDirs() + cfg.Consensus.CreateEmptyBlocks = true + cfg.Consensus.CreateEmptyBlocksInterval = time.Duration(0) + cfg.RPC.ListenAddress = "tcp://127.0.0.1:0" + cfg.P2P.ListenAddress = "tcp://127.0.0.1:0" + } + + // use mocked pv + nodekey := &p2p.NodeKey{ + PrivKey: ed25519.GenPrivKey(), + } + priv := bft.NewMockPVWithParams(nodekey.PrivKey, false, false) + + // setup geeneis + gen := &bft.GenesisDoc{} + { + + gen.GenesisTime = time.Now() + + // cfg.chainID = "tendermint_test" + gen.ChainID = cfg.ChainID() + + // XXX(gfanton): Is some a default needed here ? + // if icfg.ConsensusParams.Block == nil { + // icfg.ConsensusParams.Block = &abci.BlockParams{ + // // TODO: update limits based on config + // MaxTxBytes: 1000000, // 1MB, + // MaxDataBytes: 2000000, // 2MB, + // MaxGas: 10000000, // 10M gas + // TimeIotaMS: 100, // 100ms + // } + // } + gen.ConsensusParams = icfg.ConsensusParams + + pk := priv.GetPubKey() + gen.Validators = []bft.GenesisValidator{ + { + Address: pk.Address(), + PubKey: pk, + Power: 10, + Name: "testvalidator", + }, + } + + for _, validator := range icfg.GenesisValidator { + gen.Validators = append(gen.Validators, validator) + } + } + + txs, err := icfg.loadPackages() + if err != nil { + return nil, fmt.Errorf("uanble to load genesis packages: %w", err) + } + + txs = append(txs, icfg.GenesisTXs...) + + gen.AppState = GnoGenesisState{ + Balances: Balances(icfg.Balances).Strings(), + Txs: txs, + } + + gnoApp, err := NewAppWithOptions(&AppOptions{ + Logger: logger, + GnoRootDir: icfg.RootDir, + SkipFailingGenesisTxs: icfg.SkipFailingGenesisTxs, + MaxCycles: icfg.GenesisMaxVMCycles, + DB: db.NewMemDB(), + }) + if err != nil { + return nil, fmt.Errorf("error in creating new app: %w", err) + } + + cfg.LocalApp = gnoApp + + // Get app client creator. + appClientCreator := proxy.DefaultClientCreator( + cfg.LocalApp, + cfg.ProxyApp, + cfg.ABCI, + cfg.DBDir(), + ) + + // Create genesis factory. + genProvider := func() (*types.GenesisDoc, error) { + return gen, nil + } + + return node.NewNode(cfg, + priv, nodekey, + appClientCreator, + genProvider, + node.DefaultDBProvider, + logger, + ) +} + +type PackagePath struct { + Creator bft.Address + Deposit std.Coins + Fee std.Fee + Path string +} + +func (p PackagePath) load() ([]std.Tx, error) { + if p.Creator.IsZero() { + return nil, errors.New("empty creator address") + } + + if p.Path == "" { + return nil, errors.New("empty package path") + } + + // list all packages from target path + pkgs, err := gnomod.ListPkgs(p.Path) + if err != nil { + return nil, fmt.Errorf("listing gno packages: %w", err) + } + + // Sort packages by dependencies. + sortedPkgs, err := pkgs.Sort() + if err != nil { + return nil, fmt.Errorf("sorting packages: %w", err) + } + + // Filter out draft packages. + nonDraftPkgs := sortedPkgs.GetNonDraftPkgs() + txs := []std.Tx{} + for _, pkg := range nonDraftPkgs { + // open files in directory as MemPackage. + memPkg := gno.ReadMemPackage(pkg.Dir, pkg.Name) + + // create transaction + tx := std.Tx{ + Fee: p.Fee, + Msgs: []std.Msg{ + vmm.MsgAddPackage{ + Creator: p.Creator, + Package: memPkg, + // XXX: add deposit option + Deposit: p.Deposit, + }, + }, + } + + tx.Signatures = make([]std.Signature, len(tx.GetSigners())) + txs = append(txs, tx) + } + + return txs, nil +} + +func WaitForReadiness(n *node.Node) <-chan struct{} { + go func() { + + }() +} + +// func loadGenesisTxs( +// path string, +// chainID string, +// genesisRemote string, +// ) []std.Tx { +// txs := []std.Tx{} +// txsBz := osm.MustReadFile(path) +// txsLines := strings.Split(string(txsBz), "\n") +// for _, txLine := range txsLines { +// if txLine == "" { +// continue // skip empty line +// } + +// // patch the TX +// txLine = strings.ReplaceAll(txLine, "%%CHAINID%%", chainID) +// txLine = strings.ReplaceAll(txLine, "%%REMOTE%%", genesisRemote) + +// var tx std.Tx +// amino.MustUnmarshalJSON([]byte(txLine), &tx) +// txs = append(txs, tx) +// } + +// return txs +// } + +// func setupTestingGenesis(gnoDataDir string, cfg *config.Config, icfg *IntegrationConfig) error { +// genesisFilePath := filepath.Join(gnoDataDir, cfg.Genesis) +// osm.EnsureDir(filepath.Dir(genesisFilePath), 0o700) +// if !osm.FileExists(genesisFilePath) { +// genesisTxs := loadGenesisTxs(icfg.GenesisTxsFile, icfg.ChainID, icfg.GenesisRemote) +// pvPub := priv.GetPubKey() + +// gen := &bft.GenesisDoc{ +// GenesisTime: time.Now(), +// ChainID: icfg.ChainID, +// ConsensusParams: abci.ConsensusParams{ +// Block: &abci.BlockParams{ +// // TODO: update limits. +// MaxTxBytes: 1000000, // 1MB, +// MaxDataBytes: 2000000, // 2MB, +// MaxGas: 10000000, // 10M gas +// TimeIotaMS: 100, // 100ms +// }, +// }, +// Validators: []bft.GenesisValidator{ +// { +// Address: pvPub.Address(), +// PubKey: pvPub, +// Power: 10, +// Name: "testvalidator", +// }, +// }, +// } + +// // Load distribution. +// balances := loadGenesisBalances(icfg.GenesisBalancesFile) + +// // Load initial packages from examples. +// // XXX: we should be able to config this +// test1 := crypto.MustAddressFromString(test1Addr) +// txs := []std.Tx{} + +// // List initial packages to load from examples. +// // println(filepath.Join(gnoRootDir, "examples")) + +// // load genesis txs from file. +// txs = append(txs, genesisTxs...) + +// // construct genesis AppState. +// gen.AppState = GnoGenesisState{ +// Balances: balances, +// Txs: txs, +// } + +// writeGenesisFile(gen, genesisFilePath) +// } + +// return nil +// } + +// func loadGenesisBalances(path string) []string { +// // each balance is in the form: g1xxxxxxxxxxxxxxxx=100000ugnot +// balances := []string{} +// content := osm.MustReadFile(path) +// lines := strings.Split(string(content), "\n") +// for _, line := range lines { +// line = strings.TrimSpace(line) + +// // remove comments. +// line = strings.Split(line, "#")[0] +// line = strings.TrimSpace(line) + +// // skip empty lines. +// if line == "" { +// continue +// } + +// parts := strings.Split(line, "=") +// if len(parts) != 2 { +// panic("invalid genesis_balance line: " + line) +// } + +// balances = append(balances, line) +// } +// return balances +// } + +// func writeGenesisFile(gen *bft.GenesisDoc, filePath string) { +// err := gen.SaveAs(filePath) +// if err != nil { +// panic(err) +// } +// } diff --git a/gno.land/pkg/gnoland/inmemory_test.go b/gno.land/pkg/gnoland/inmemory_test.go new file mode 100644 index 00000000000..9c2b79dab7d --- /dev/null +++ b/gno.land/pkg/gnoland/inmemory_test.go @@ -0,0 +1,16 @@ +package gnoland + +import ( + "testing" + + "github.com/gnolang/gno/tm2/pkg/log" + "github.com/jaekwon/testify/require" +) + +func TestNewInMemory(t *testing.T) { + logger := log.TestingLogger() + + node, err := NewInMemory(logger, InMemoryConfig{}) + require.NoError(t, err) + require.NotNil(t, node) +} diff --git a/gno.land/pkg/gnoland/types.go b/gno.land/pkg/gnoland/types.go index 1c762366ae9..463638d5901 100644 --- a/gno.land/pkg/gnoland/types.go +++ b/gno.land/pkg/gnoland/types.go @@ -1,6 +1,9 @@ package gnoland import ( + "fmt" + + bft "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/std" ) @@ -16,3 +19,22 @@ type GnoGenesisState struct { Balances []string `json:"balances"` Txs []std.Tx `json:"txs"` } + +type Balance struct { + Address bft.Address + Value std.Coin +} + +func (b Balance) String() string { + return fmt.Sprintf("%s=%s", b.Address.String(), b.Value.String()) +} + +type Balances []Balance + +func (bs Balances) Strings() []string { + bss := make([]string, len(bs)) + for i, balance := range bs { + bss[i] = balance.String() + } + return bss +} From db5a4c2fae7370bc067a836f2b3f586d27fa9311 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Wed, 11 Oct 2023 22:20:31 +0200 Subject: [PATCH 02/38] Revert "fix(make): disable _test.gnoweb temporarily (#1223)" This reverts commit dce345f31b8fc6be3e831778983345d36f01e31f. Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- .github/workflows/gnoland.yml | 8 +++----- gno.land/Makefile | 4 +--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/gnoland.yml b/.github/workflows/gnoland.yml index 95cb5fa8ce0..2b38f254a13 100644 --- a/.github/workflows/gnoland.yml +++ b/.github/workflows/gnoland.yml @@ -60,9 +60,7 @@ jobs: - _test.gnoland - _test.gnokey - _test.pkgs - # XXX: test broken, should be rewritten to run an inmemory localnode - # Re-add to makefile when fixed. Tracked here: https://github.com/gnolang/gno/issues/1222 - #- _test.gnoweb + #- _test.gnoweb # this test should be rewritten to run an inmemory localnode runs-on: ubuntu-latest timeout-minutes: 15 steps: @@ -78,7 +76,7 @@ jobs: export LOG_DIR="${{ runner.temp }}/logs/test-${{ matrix.goversion }}-gnoland" make ${{ matrix.args }} - name: Upload Test Log - if: always() + if: always() uses: actions/upload-artifact@v3 with: name: logs-test-gnoland-go${{ matrix.goversion }} @@ -101,7 +99,7 @@ jobs: uses: codecov/codecov-action@v3 with: directory: ${{ runner.temp }}/coverage - token: ${{ secrets.CODECOV_TOKEN }} + token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: ${{ github.repository == 'gnolang/gno' }} docker-integration: diff --git a/gno.land/Makefile b/gno.land/Makefile index 1fd1aaa1f78..e794bb58174 100644 --- a/gno.land/Makefile +++ b/gno.land/Makefile @@ -43,9 +43,7 @@ fmt: ######################################## # Test suite .PHONY: test -test: _test.gnoland _test.gnokey _test.pkgs -# XXX: _test.gnoweb is currently disabled. If fixed, re-enable here and in CI. -# https://github.com/gnolang/gno/issues/1222 +test: _test.gnoland _test.gnoweb _test.gnokey _test.pkgs GOTEST_FLAGS ?= -v -p 1 -timeout=30m From 3b71cc6acd2ec44c916c0ab6c7b3338f751bfd75 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 12 Oct 2023 17:26:24 +0200 Subject: [PATCH 03/38] feat: add integration node Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/pkg/gnoland/app.go | 5 +- gno.land/pkg/gnoland/genesis.go | 79 ++++ gno.land/pkg/gnoland/inmemory.go | 326 ----------------- gno.land/pkg/gnoland/node.go | 115 ++++++ gno.land/pkg/gnoland/types.go | 53 ++- gno.land/pkg/integration/gnoland.go | 336 ------------------ gno.land/pkg/integration/node.go | 206 +++++++++++ gno.land/pkg/integration/node_inmemory.go | 5 + .../node_test.go} | 4 +- .../pkg/integration/testing_integration.go | 81 ++++- 10 files changed, 526 insertions(+), 684 deletions(-) create mode 100644 gno.land/pkg/gnoland/genesis.go delete mode 100644 gno.land/pkg/gnoland/inmemory.go create mode 100644 gno.land/pkg/gnoland/node.go delete mode 100644 gno.land/pkg/integration/gnoland.go create mode 100644 gno.land/pkg/integration/node.go create mode 100644 gno.land/pkg/integration/node_inmemory.go rename gno.land/pkg/{gnoland/inmemory_test.go => integration/node_test.go} (76%) diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index fab57472fcd..6ac13d4ff99 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -147,10 +147,9 @@ func InitChainer(baseApp *sdk.BaseApp, acctKpr auth.AccountKeeperI, bankKpr bank genState := req.AppState.(GnoGenesisState) // Parse and set genesis state balances. for _, bal := range genState.Balances { - addr, coins := parseBalance(bal) - acc := acctKpr.NewAccountWithAddress(ctx, addr) + acc := acctKpr.NewAccountWithAddress(ctx, bal.Address) acctKpr.SetAccount(ctx, acc) - err := bankKpr.SetCoins(ctx, addr, coins) + err := bankKpr.SetCoins(ctx, bal.Address, bal.Value) if err != nil { panic(err) } diff --git a/gno.land/pkg/gnoland/genesis.go b/gno.land/pkg/gnoland/genesis.go new file mode 100644 index 00000000000..b155b212219 --- /dev/null +++ b/gno.land/pkg/gnoland/genesis.go @@ -0,0 +1,79 @@ +package gnoland + +import ( + "errors" + "fmt" + "strings" + + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/crypto" + osm "github.com/gnolang/gno/tm2/pkg/os" + "github.com/gnolang/gno/tm2/pkg/std" +) + +func LoadGenesisBalancesFile(path string) ([]Balance, error) { + // each balance is in the form: g1xxxxxxxxxxxxxxxx=100000ugnot + content := osm.MustReadFile(path) + lines := strings.Split(string(content), "\n") + + balances := make([]Balance, 0, len(lines)) + for _, line := range lines { + line = strings.TrimSpace(line) + + // remove comments. + line = strings.Split(line, "#")[0] + line = strings.TrimSpace(line) + + // skip empty lines. + if line == "" { + continue + } + + parts := strings.Split(line, "=") //
= + if len(parts) != 2 { + return nil, errors.New("invalid genesis_balance line: " + line) + } + + addr, err := crypto.AddressFromBech32(parts[0]) + if err != nil { + return nil, fmt.Errorf("invalid balance addr %s: %w", parts[0], err) + } + + coins, err := std.ParseCoins(parts[1]) + if err != nil { + return nil, fmt.Errorf("invalid balance coins %s: %w", parts[1], err) + } + + balances = append(balances, Balance{ + Address: addr, + Value: coins, + }) + } + + return balances, nil +} + +// XXX: we can do something better here +func LoadGenesisTxsFile(path string, chainID string, genesisRemote string) ([]std.Tx, error) { + txs := []std.Tx{} + txsBz := osm.MustReadFile(path) + txsLines := strings.Split(string(txsBz), "\n") + for _, txLine := range txsLines { + if txLine == "" { + continue // Skip empty line. + } + + // Patch the TX. + txLine = strings.ReplaceAll(txLine, "%%CHAINID%%", chainID) + txLine = strings.ReplaceAll(txLine, "%%REMOTE%%", genesisRemote) + + var tx std.Tx + if err := amino.UnmarshalJSON([]byte(txLine), &tx); err != nil { + return nil, fmt.Errorf("unable to Unmarshall txs file: %w", err) + } + + txs = append(txs, tx) + } + + return txs, nil +} diff --git a/gno.land/pkg/gnoland/inmemory.go b/gno.land/pkg/gnoland/inmemory.go deleted file mode 100644 index d1971ad0c8b..00000000000 --- a/gno.land/pkg/gnoland/inmemory.go +++ /dev/null @@ -1,326 +0,0 @@ -package gnoland - -import ( - "errors" - "fmt" - "time" - - vmm "github.com/gnolang/gno/gno.land/pkg/sdk/vm" - gno "github.com/gnolang/gno/gnovm/pkg/gnolang" - "github.com/gnolang/gno/gnovm/pkg/gnomod" - abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" - "github.com/gnolang/gno/tm2/pkg/bft/config" - "github.com/gnolang/gno/tm2/pkg/bft/node" - "github.com/gnolang/gno/tm2/pkg/bft/proxy" - "github.com/gnolang/gno/tm2/pkg/bft/types" - bft "github.com/gnolang/gno/tm2/pkg/bft/types" - "github.com/gnolang/gno/tm2/pkg/crypto/ed25519" - "github.com/gnolang/gno/tm2/pkg/db" - "github.com/gnolang/gno/tm2/pkg/log" - "github.com/gnolang/gno/tm2/pkg/p2p" - "github.com/gnolang/gno/tm2/pkg/std" -) - -type InMemoryConfig struct { - RootDir string - ConsensusParams abci.ConsensusParams - GenesisValidator []bft.GenesisValidator - Packages []PackagePath - Balances []Balance - GenesisTXs []std.Tx - SkipFailingGenesisTxs bool - GenesisMaxVMCycles int64 -} - -func (im *InMemoryConfig) loadPackages() ([]std.Tx, error) { - txs := []std.Tx{} - for _, pkg := range im.Packages { - tx, err := pkg.load() - if err != nil { - return nil, fmt.Errorf("unable to load packages: %w", err) - } - txs = append(txs, tx...) - - } - return txs, nil -} - -func NewInMemory(logger log.Logger, icfg InMemoryConfig) (*node.Node, error) { - if icfg.RootDir == "" { - // XXX: Should return an error here ? - icfg.RootDir = MustGuessGnoRootDir() - } - - // Setup testing config - cfg := config.TestConfig().SetRootDir(icfg.RootDir) - { - cfg.EnsureDirs() - cfg.Consensus.CreateEmptyBlocks = true - cfg.Consensus.CreateEmptyBlocksInterval = time.Duration(0) - cfg.RPC.ListenAddress = "tcp://127.0.0.1:0" - cfg.P2P.ListenAddress = "tcp://127.0.0.1:0" - } - - // use mocked pv - nodekey := &p2p.NodeKey{ - PrivKey: ed25519.GenPrivKey(), - } - priv := bft.NewMockPVWithParams(nodekey.PrivKey, false, false) - - // setup geeneis - gen := &bft.GenesisDoc{} - { - - gen.GenesisTime = time.Now() - - // cfg.chainID = "tendermint_test" - gen.ChainID = cfg.ChainID() - - // XXX(gfanton): Is some a default needed here ? - // if icfg.ConsensusParams.Block == nil { - // icfg.ConsensusParams.Block = &abci.BlockParams{ - // // TODO: update limits based on config - // MaxTxBytes: 1000000, // 1MB, - // MaxDataBytes: 2000000, // 2MB, - // MaxGas: 10000000, // 10M gas - // TimeIotaMS: 100, // 100ms - // } - // } - gen.ConsensusParams = icfg.ConsensusParams - - pk := priv.GetPubKey() - gen.Validators = []bft.GenesisValidator{ - { - Address: pk.Address(), - PubKey: pk, - Power: 10, - Name: "testvalidator", - }, - } - - for _, validator := range icfg.GenesisValidator { - gen.Validators = append(gen.Validators, validator) - } - } - - txs, err := icfg.loadPackages() - if err != nil { - return nil, fmt.Errorf("uanble to load genesis packages: %w", err) - } - - txs = append(txs, icfg.GenesisTXs...) - - gen.AppState = GnoGenesisState{ - Balances: Balances(icfg.Balances).Strings(), - Txs: txs, - } - - gnoApp, err := NewAppWithOptions(&AppOptions{ - Logger: logger, - GnoRootDir: icfg.RootDir, - SkipFailingGenesisTxs: icfg.SkipFailingGenesisTxs, - MaxCycles: icfg.GenesisMaxVMCycles, - DB: db.NewMemDB(), - }) - if err != nil { - return nil, fmt.Errorf("error in creating new app: %w", err) - } - - cfg.LocalApp = gnoApp - - // Get app client creator. - appClientCreator := proxy.DefaultClientCreator( - cfg.LocalApp, - cfg.ProxyApp, - cfg.ABCI, - cfg.DBDir(), - ) - - // Create genesis factory. - genProvider := func() (*types.GenesisDoc, error) { - return gen, nil - } - - return node.NewNode(cfg, - priv, nodekey, - appClientCreator, - genProvider, - node.DefaultDBProvider, - logger, - ) -} - -type PackagePath struct { - Creator bft.Address - Deposit std.Coins - Fee std.Fee - Path string -} - -func (p PackagePath) load() ([]std.Tx, error) { - if p.Creator.IsZero() { - return nil, errors.New("empty creator address") - } - - if p.Path == "" { - return nil, errors.New("empty package path") - } - - // list all packages from target path - pkgs, err := gnomod.ListPkgs(p.Path) - if err != nil { - return nil, fmt.Errorf("listing gno packages: %w", err) - } - - // Sort packages by dependencies. - sortedPkgs, err := pkgs.Sort() - if err != nil { - return nil, fmt.Errorf("sorting packages: %w", err) - } - - // Filter out draft packages. - nonDraftPkgs := sortedPkgs.GetNonDraftPkgs() - txs := []std.Tx{} - for _, pkg := range nonDraftPkgs { - // open files in directory as MemPackage. - memPkg := gno.ReadMemPackage(pkg.Dir, pkg.Name) - - // create transaction - tx := std.Tx{ - Fee: p.Fee, - Msgs: []std.Msg{ - vmm.MsgAddPackage{ - Creator: p.Creator, - Package: memPkg, - // XXX: add deposit option - Deposit: p.Deposit, - }, - }, - } - - tx.Signatures = make([]std.Signature, len(tx.GetSigners())) - txs = append(txs, tx) - } - - return txs, nil -} - -func WaitForReadiness(n *node.Node) <-chan struct{} { - go func() { - - }() -} - -// func loadGenesisTxs( -// path string, -// chainID string, -// genesisRemote string, -// ) []std.Tx { -// txs := []std.Tx{} -// txsBz := osm.MustReadFile(path) -// txsLines := strings.Split(string(txsBz), "\n") -// for _, txLine := range txsLines { -// if txLine == "" { -// continue // skip empty line -// } - -// // patch the TX -// txLine = strings.ReplaceAll(txLine, "%%CHAINID%%", chainID) -// txLine = strings.ReplaceAll(txLine, "%%REMOTE%%", genesisRemote) - -// var tx std.Tx -// amino.MustUnmarshalJSON([]byte(txLine), &tx) -// txs = append(txs, tx) -// } - -// return txs -// } - -// func setupTestingGenesis(gnoDataDir string, cfg *config.Config, icfg *IntegrationConfig) error { -// genesisFilePath := filepath.Join(gnoDataDir, cfg.Genesis) -// osm.EnsureDir(filepath.Dir(genesisFilePath), 0o700) -// if !osm.FileExists(genesisFilePath) { -// genesisTxs := loadGenesisTxs(icfg.GenesisTxsFile, icfg.ChainID, icfg.GenesisRemote) -// pvPub := priv.GetPubKey() - -// gen := &bft.GenesisDoc{ -// GenesisTime: time.Now(), -// ChainID: icfg.ChainID, -// ConsensusParams: abci.ConsensusParams{ -// Block: &abci.BlockParams{ -// // TODO: update limits. -// MaxTxBytes: 1000000, // 1MB, -// MaxDataBytes: 2000000, // 2MB, -// MaxGas: 10000000, // 10M gas -// TimeIotaMS: 100, // 100ms -// }, -// }, -// Validators: []bft.GenesisValidator{ -// { -// Address: pvPub.Address(), -// PubKey: pvPub, -// Power: 10, -// Name: "testvalidator", -// }, -// }, -// } - -// // Load distribution. -// balances := loadGenesisBalances(icfg.GenesisBalancesFile) - -// // Load initial packages from examples. -// // XXX: we should be able to config this -// test1 := crypto.MustAddressFromString(test1Addr) -// txs := []std.Tx{} - -// // List initial packages to load from examples. -// // println(filepath.Join(gnoRootDir, "examples")) - -// // load genesis txs from file. -// txs = append(txs, genesisTxs...) - -// // construct genesis AppState. -// gen.AppState = GnoGenesisState{ -// Balances: balances, -// Txs: txs, -// } - -// writeGenesisFile(gen, genesisFilePath) -// } - -// return nil -// } - -// func loadGenesisBalances(path string) []string { -// // each balance is in the form: g1xxxxxxxxxxxxxxxx=100000ugnot -// balances := []string{} -// content := osm.MustReadFile(path) -// lines := strings.Split(string(content), "\n") -// for _, line := range lines { -// line = strings.TrimSpace(line) - -// // remove comments. -// line = strings.Split(line, "#")[0] -// line = strings.TrimSpace(line) - -// // skip empty lines. -// if line == "" { -// continue -// } - -// parts := strings.Split(line, "=") -// if len(parts) != 2 { -// panic("invalid genesis_balance line: " + line) -// } - -// balances = append(balances, line) -// } -// return balances -// } - -// func writeGenesisFile(gen *bft.GenesisDoc, filePath string) { -// err := gen.SaveAs(filePath) -// if err != nil { -// panic(err) -// } -// } diff --git a/gno.land/pkg/gnoland/node.go b/gno.land/pkg/gnoland/node.go new file mode 100644 index 00000000000..d9e54f35cd7 --- /dev/null +++ b/gno.land/pkg/gnoland/node.go @@ -0,0 +1,115 @@ +package gnoland + +// func loadGenesisTxs( +// path string, +// chainID string, +// genesisRemote string, +// ) []std.Tx { +// txs := []std.Tx{} +// txsBz := osm.MustReadFile(path) +// txsLines := strings.Split(string(txsBz), "\n") +// for _, txLine := range txsLines { +// if txLine == "" { +// continue // skip empty line +// } + +// // patch the TX +// txLine = strings.ReplaceAll(txLine, "%%CHAINID%%", chainID) +// txLine = strings.ReplaceAll(txLine, "%%REMOTE%%", genesisRemote) + +// var tx std.Tx +// amino.MustUnmarshalJSON([]byte(txLine), &tx) +// txs = append(txs, tx) +// } + +// return txs +// } + +// func setupTestingGenesis(gnoDataDir string, cfg *config.Config, icfg *IntegrationConfig) error { +// genesisFilePath := filepath.Join(gnoDataDir, cfg.Genesis) +// osm.EnsureDir(filepath.Dir(genesisFilePath), 0o700) +// if !osm.FileExists(genesisFilePath) { +// genesisTxs := loadGenesisTxs(icfg.GenesisTxsFile, icfg.ChainID, icfg.GenesisRemote) +// pvPub := priv.GetPubKey() + +// gen := &bft.GenesisDoc{ +// GenesisTime: time.Now(), +// ChainID: icfg.ChainID, +// ConsensusParams: abci.ConsensusParams{ +// Block: &abci.BlockParams{ +// // TODO: update limits. +// MaxTxBytes: 1000000, // 1MB, +// MaxDataBytes: 2000000, // 2MB, +// MaxGas: 10000000, // 10M gas +// TimeIotaMS: 100, // 100ms +// }, +// }, +// Validators: []bft.GenesisValidator{ +// { +// Address: pvPub.Address(), +// PubKey: pvPub, +// Power: 10, +// Name: "testvalidator", +// }, +// }, +// } + +// // Load distribution. +// balances := loadGenesisBalances(icfg.GenesisBalancesFile) + +// // Load initial packages from examples. +// // XXX: we should be able to config this +// test1 := crypto.MustAddressFromString(test1Addr) +// txs := []std.Tx{} + +// // List initial packages to load from examples. +// // println(filepath.Join(gnoRootDir, "examples")) + +// // load genesis txs from file. +// txs = append(txs, genesisTxs...) + +// // construct genesis AppState. +// gen.AppState = GnoGenesisState{ +// Balances: balances, +// Txs: txs, +// } + +// writeGenesisFile(gen, genesisFilePath) +// } + +// return nil +// } + +// func loadGenesisBalances(path string) []string { +// // each balance is in the form: g1xxxxxxxxxxxxxxxx=100000ugnot +// balances := []string{} +// content := osm.MustReadFile(path) +// lines := strings.Split(string(content), "\n") +// for _, line := range lines { +// line = strings.TrimSpace(line) + +// // remove comments. +// line = strings.Split(line, "#")[0] +// line = strings.TrimSpace(line) + +// // skip empty lines. +// if line == "" { +// continue +// } + +// parts := strings.Split(line, "=") +// if len(parts) != 2 { +// panic("invalid genesis_balance line: " + line) +// } + +// balances = append(balances, line) +// } +// return balances +// } + +// func writeGenesisFile(gen *bft.GenesisDoc, filePath string) { +// err := gen.SaveAs(filePath) +// if err != nil { +// panic(err) +// } +// } diff --git a/gno.land/pkg/gnoland/types.go b/gno.land/pkg/gnoland/types.go index 463638d5901..721f2c726f2 100644 --- a/gno.land/pkg/gnoland/types.go +++ b/gno.land/pkg/gnoland/types.go @@ -1,9 +1,12 @@ package gnoland import ( + "errors" "fmt" + "strings" bft "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/std" ) @@ -16,25 +19,53 @@ func ProtoGnoAccount() std.Account { } type GnoGenesisState struct { - Balances []string `json:"balances"` - Txs []std.Tx `json:"txs"` + Balances []Balance `json:"balances"` + Txs []std.Tx `json:"txs"` } type Balance struct { Address bft.Address - Value std.Coin + Value std.Coins +} + +func (b *Balance) Parse(line string) error { + parts := strings.Split(strings.TrimSpace(line), "=") //
= + if len(parts) != 2 { + return errors.New("invalid genesis_balance line: " + line) + } + + var err error + + b.Address, err = crypto.AddressFromBech32(parts[0]) + if err != nil { + return fmt.Errorf("invalid balance addr %s: %w", parts[0], err) + } + + b.Value, err = std.ParseCoins(parts[1]) + if err != nil { + return fmt.Errorf("invalid balance coins %s: %w", parts[1], err) + } + + return nil +} + +func (b *Balance) UnmarshalJSON(data []byte) error { + return b.Parse(string(data)) +} +func (b *Balance) Marshaljson() ([]byte, error) { + return []byte(b.String()), nil } func (b Balance) String() string { return fmt.Sprintf("%s=%s", b.Address.String(), b.Value.String()) } -type Balances []Balance +// type Balances []Balance -func (bs Balances) Strings() []string { - bss := make([]string, len(bs)) - for i, balance := range bs { - bss[i] = balance.String() - } - return bss -} +// func (bs Balances) Strings() []string { +// bss := make([]string, len(bs)) +// for i, balance := range bs { +// bss[i] = balance.String() +// } +// return bss +// } diff --git a/gno.land/pkg/integration/gnoland.go b/gno.land/pkg/integration/gnoland.go deleted file mode 100644 index c4fee341bfc..00000000000 --- a/gno.land/pkg/integration/gnoland.go +++ /dev/null @@ -1,336 +0,0 @@ -package integration - -import ( - "flag" - "fmt" - "path/filepath" - "strings" - "testing" - "time" - - "github.com/gnolang/gno/gno.land/pkg/gnoland" - vmm "github.com/gnolang/gno/gno.land/pkg/sdk/vm" - gno "github.com/gnolang/gno/gnovm/pkg/gnolang" - "github.com/gnolang/gno/gnovm/pkg/gnomod" - "github.com/gnolang/gno/tm2/pkg/amino" - abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" - "github.com/gnolang/gno/tm2/pkg/bft/config" - "github.com/gnolang/gno/tm2/pkg/bft/node" - "github.com/gnolang/gno/tm2/pkg/bft/privval" - bft "github.com/gnolang/gno/tm2/pkg/bft/types" - "github.com/gnolang/gno/tm2/pkg/crypto" - "github.com/gnolang/gno/tm2/pkg/db" - "github.com/gnolang/gno/tm2/pkg/log" - osm "github.com/gnolang/gno/tm2/pkg/os" - "github.com/gnolang/gno/tm2/pkg/std" - "github.com/rogpeppe/go-internal/testscript" -) - -type IntegrationConfig struct { - SkipFailingGenesisTxs bool - SkipStart bool - GenesisBalancesFile string - GenesisTxsFile string - ChainID string - GenesisRemote string - RootDir string - GenesisMaxVMCycles int64 - Config string -} - -// NOTE: This is a copy of gnoland actual flags. -// XXX: A lot this make no sense for integration. -func (c *IntegrationConfig) RegisterFlags(fs *flag.FlagSet) { - fs.BoolVar( - &c.SkipFailingGenesisTxs, - "skip-failing-genesis-txs", - false, - "don't panic when replaying invalid genesis txs", - ) - fs.BoolVar( - &c.SkipStart, - "skip-start", - false, - "quit after initialization, don't start the node", - ) - - fs.StringVar( - &c.GenesisBalancesFile, - "genesis-balances-file", - "./genesis/genesis_balances.txt", - "initial distribution file", - ) - - fs.StringVar( - &c.GenesisTxsFile, - "genesis-txs-file", - "./genesis/genesis_txs.txt", - "initial txs to replay", - ) - - fs.StringVar( - &c.ChainID, - "chainid", - "dev", - "the ID of the chain", - ) - - fs.StringVar( - &c.RootDir, - "root-dir", - "testdir", - "directory for config and data", - ) - - fs.StringVar( - &c.GenesisRemote, - "genesis-remote", - "localhost:26657", - "replacement for '%%REMOTE%%' in genesis", - ) - - fs.Int64Var( - &c.GenesisMaxVMCycles, - "genesis-max-vm-cycles", - 10_000_000, - "set maximum allowed vm cycles per operation. Zero means no limit.", - ) -} - -func execTestingGnoland(t *testing.T, logger log.Logger, gnoDataDir, gnoRootDir string, args []string) (*node.Node, error) { - t.Helper() - - // Setup start config. - icfg := &IntegrationConfig{} - { - fs := flag.NewFlagSet("start", flag.ExitOnError) - icfg.RegisterFlags(fs) - - // Override default value for flags. - fs.VisitAll(func(f *flag.Flag) { - switch f.Name { - case "root-dir": - f.DefValue = gnoDataDir - case "chainid": - f.DefValue = "tendermint_test" - case "genesis-balances-file": - f.DefValue = filepath.Join(gnoRootDir, "gno.land", "genesis", "genesis_balances.txt") - case "genesis-txs-file": - f.DefValue = filepath.Join(gnoRootDir, "gno.land", "genesis", "genesis_txs.txt") - default: - return - } - - f.Value.Set(f.DefValue) - }) - - if err := fs.Parse(args); err != nil { - return nil, fmt.Errorf("unable to parse flags: %w", err) - } - } - - // Setup testing config. - cfg := config.TestConfig().SetRootDir(gnoDataDir) - { - cfg.EnsureDirs() - cfg.Consensus.CreateEmptyBlocks = true - cfg.Consensus.CreateEmptyBlocksInterval = time.Duration(0) - cfg.RPC.ListenAddress = "tcp://127.0.0.1:0" - cfg.P2P.ListenAddress = "tcp://127.0.0.1:0" - } - - // Prepare genesis. - if err := setupTestingGenesis(gnoDataDir, cfg, icfg, gnoRootDir); err != nil { - return nil, err - } - - // Create application and node. - return createAppAndNode(cfg, logger, gnoRootDir, icfg) -} - -func setupTestingGenesis(gnoDataDir string, cfg *config.Config, icfg *IntegrationConfig, gnoRootDir string) error { - newPrivValKey := cfg.PrivValidatorKeyFile() - newPrivValState := cfg.PrivValidatorStateFile() - priv := privval.LoadOrGenFilePV(newPrivValKey, newPrivValState) - - genesisFilePath := filepath.Join(gnoDataDir, cfg.Genesis) - genesisDirPath := filepath.Dir(genesisFilePath) - if err := osm.EnsureDir(genesisDirPath, 0o700); err != nil { - return fmt.Errorf("unable to ensure directory %q: %w", genesisDirPath, err) - } - - genesisTxs := loadGenesisTxs(icfg.GenesisTxsFile, icfg.ChainID, icfg.GenesisRemote) - pvPub := priv.GetPubKey() - - gen := &bft.GenesisDoc{ - GenesisTime: time.Now(), - ChainID: icfg.ChainID, - ConsensusParams: abci.ConsensusParams{ - Block: &abci.BlockParams{ - // TODO: update limits. - MaxTxBytes: 1000000, // 1MB, - MaxDataBytes: 2000000, // 2MB, - MaxGas: 10000000, // 10M gas - TimeIotaMS: 100, // 100ms - }, - }, - Validators: []bft.GenesisValidator{ - { - Address: pvPub.Address(), - PubKey: pvPub, - Power: 10, - Name: "testvalidator", - }, - }, - } - - // Load distribution. - balances := loadGenesisBalances(icfg.GenesisBalancesFile) - - // Load initial packages from examples. - // XXX: We should be able to config this. - test1 := crypto.MustAddressFromString(test1Addr) - txs := []std.Tx{} - - // List initial packages to load from examples. - // println(filepath.Join(gnoRootDir, "examples")) - pkgs, err := gnomod.ListPkgs(filepath.Join(gnoRootDir, "examples")) - if err != nil { - return fmt.Errorf("listing gno packages: %w", err) - } - - // Sort packages by dependencies. - sortedPkgs, err := pkgs.Sort() - if err != nil { - return fmt.Errorf("sorting packages: %w", err) - } - - // Filter out draft packages. - nonDraftPkgs := sortedPkgs.GetNonDraftPkgs() - - for _, pkg := range nonDraftPkgs { - // Open files in directory as MemPackage. - memPkg := gno.ReadMemPackage(pkg.Dir, pkg.Name) - - var tx std.Tx - tx.Msgs = []std.Msg{ - vmm.MsgAddPackage{ - Creator: test1, - Package: memPkg, - Deposit: nil, - }, - } - - // XXX: Add fee flag ? - // Or maybe reduce fee to the minimum ? - tx.Fee = std.NewFee(50000, std.MustParseCoin("1000000ugnot")) - tx.Signatures = make([]std.Signature, len(tx.GetSigners())) - txs = append(txs, tx) - } - - // Load genesis txs from file. - txs = append(txs, genesisTxs...) - - // Construct genesis AppState. - gen.AppState = gnoland.GnoGenesisState{ - Balances: balances, - Txs: txs, - } - - writeGenesisFile(gen, genesisFilePath) - - return nil -} - -func createAppAndNode(cfg *config.Config, logger log.Logger, gnoRootDir string, icfg *IntegrationConfig) (*node.Node, error) { - gnoApp, err := gnoland.NewAppWithOptions(&gnoland.AppOptions{ - Logger: logger, - GnoRootDir: gnoRootDir, - SkipFailingGenesisTxs: icfg.SkipFailingGenesisTxs, - MaxCycles: icfg.GenesisMaxVMCycles, - DB: db.NewMemDB(), - }) - if err != nil { - return nil, fmt.Errorf("error in creating new app: %w", err) - } - - cfg.LocalApp = gnoApp - node, err := node.DefaultNewNode(cfg, logger) - if err != nil { - return nil, fmt.Errorf("error in creating node: %w", err) - } - - return node, node.Start() -} - -func tsValidateError(ts *testscript.TestScript, cmd string, neg bool, err error) { - if err != nil { - fmt.Fprintf(ts.Stderr(), "%q error: %v\n", cmd, err) - if !neg { - ts.Fatalf("unexpected %q command failure: %s", cmd, err) - } - } else { - if neg { - ts.Fatalf("unexpected %s command success", cmd) - } - } -} - -func loadGenesisTxs( - path string, - chainID string, - genesisRemote string, -) []std.Tx { - txs := []std.Tx{} - txsBz := osm.MustReadFile(path) - txsLines := strings.Split(string(txsBz), "\n") - for _, txLine := range txsLines { - if txLine == "" { - continue // Skip empty line. - } - - // Patch the TX. - txLine = strings.ReplaceAll(txLine, "%%CHAINID%%", chainID) - txLine = strings.ReplaceAll(txLine, "%%REMOTE%%", genesisRemote) - - var tx std.Tx - amino.MustUnmarshalJSON([]byte(txLine), &tx) - txs = append(txs, tx) - } - - return txs -} - -func loadGenesisBalances(path string) []string { - // Each balance is in the form: g1xxxxxxxxxxxxxxxx=100000ugnot. - balances := []string{} - content := osm.MustReadFile(path) - lines := strings.Split(string(content), "\n") - for _, line := range lines { - line = strings.TrimSpace(line) - - // Remove comments. - line = strings.Split(line, "#")[0] - line = strings.TrimSpace(line) - - // Skip empty lines. - if line == "" { - continue - } - - parts := strings.Split(line, "=") - if len(parts) != 2 { - panic("invalid genesis_balance line: " + line) - } - - balances = append(balances, line) - } - return balances -} - -func writeGenesisFile(gen *bft.GenesisDoc, filePath string) { - err := gen.SaveAs(filePath) - if err != nil { - panic(err) - } -} diff --git a/gno.land/pkg/integration/node.go b/gno.land/pkg/integration/node.go new file mode 100644 index 00000000000..a0ee63a0780 --- /dev/null +++ b/gno.land/pkg/integration/node.go @@ -0,0 +1,206 @@ +package integration + +import ( + "errors" + "fmt" + "time" + + "github.com/gnolang/gno/gno.land/pkg/gnoland" + vmm "github.com/gnolang/gno/gno.land/pkg/sdk/vm" + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/gnovm/pkg/gnomod" + abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" + "github.com/gnolang/gno/tm2/pkg/bft/config" + "github.com/gnolang/gno/tm2/pkg/bft/node" + "github.com/gnolang/gno/tm2/pkg/bft/proxy" + "github.com/gnolang/gno/tm2/pkg/bft/types" + bft "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/crypto/ed25519" + "github.com/gnolang/gno/tm2/pkg/db" + "github.com/gnolang/gno/tm2/pkg/log" + "github.com/gnolang/gno/tm2/pkg/p2p" + "github.com/gnolang/gno/tm2/pkg/std" +) + +type NodeConfig struct { + BFTConfig *config.Config + ConsensusParams abci.ConsensusParams + GenesisValidator []bft.GenesisValidator + Packages []PackagePath + Balances []gnoland.Balance + GenesisTXs []std.Tx + SkipFailingGenesisTxs bool + GenesisMaxVMCycles int64 +} + +func NewNode(logger log.Logger, icfg NodeConfig) (*node.Node, error) { + bftconfig := icfg.BFTConfig + { + // Setup setup testing config + if bftconfig == nil { + bftconfig = config.TestConfig() + bftconfig.RPC.ListenAddress = "tcp://127.0.0.1:0" + bftconfig.P2P.ListenAddress = "tcp://127.0.0.1:0" + } + + // XXX: we need to get ride of this, for now needed because of stdlib + if bftconfig.RootDir == "" { + gnoRootDir := gnoland.MustGuessGnoRootDir() + bftconfig.SetRootDir(gnoRootDir) + } + } + + nodekey := &p2p.NodeKey{PrivKey: ed25519.GenPrivKey()} + priv := bft.NewMockPVWithParams(nodekey.PrivKey, false, false) + + // Setup geeneis + gen := &bft.GenesisDoc{} + { + + gen.GenesisTime = time.Now() + + // cfg.chainID = "tendermint_test" + gen.ChainID = bftconfig.ChainID() + + // XXX(gfanton): Do we need some default here ? + // if icfg.ConsensusParams.Block == nil { + // icfg.ConsensusParams.Block = &abci.BlockParams{ + // MaxTxBytes: 1000000, // 1MB, + // MaxDataBytes: 2000000, // 2MB, + // MaxGas: 10000000, // 10M gas + // TimeIotaMS: 100, // 100ms + // } + // } + gen.ConsensusParams = icfg.ConsensusParams + + pk := priv.GetPubKey() + + // start with self validator + gen.Validators = []bft.GenesisValidator{ + { + Address: pk.Address(), + PubKey: pk, + Power: 10, + Name: "rootValidator", + }, + } + + for _, validator := range icfg.GenesisValidator { + gen.Validators = append(gen.Validators, validator) + } + } + + // XXX: maybe let the user do this manually and pass it to genesisTXs + txs, err := LoadPackages(icfg.Packages) + if err != nil { + return nil, fmt.Errorf("uanble to load genesis packages: %w", err) + } + + txs = append(txs, icfg.GenesisTXs...) + + gen.AppState = gnoland.GnoGenesisState{ + Balances: icfg.Balances, + Txs: txs, + } + + gnoApp, err := gnoland.NewAppWithOptions(&gnoland.AppOptions{ + Logger: logger, + GnoRootDir: bftconfig.RootDir, + SkipFailingGenesisTxs: icfg.SkipFailingGenesisTxs, + MaxCycles: icfg.GenesisMaxVMCycles, + DB: db.NewMemDB(), + }) + if err != nil { + return nil, fmt.Errorf("error in creating new app: %w", err) + } + + bftconfig.LocalApp = gnoApp + + // Get app client creator. + appClientCreator := proxy.DefaultClientCreator( + bftconfig.LocalApp, + bftconfig.ProxyApp, + bftconfig.ABCI, + bftconfig.DBDir(), + ) + + // Create genesis factory. + genProvider := func() (*types.GenesisDoc, error) { + return gen, nil + } + + return node.NewNode(bftconfig, + priv, nodekey, + appClientCreator, + genProvider, + node.DefaultDBProvider, + logger, + ) +} + +func LoadPackages(pkgs []PackagePath) ([]std.Tx, error) { + txs := []std.Tx{} + for _, pkg := range pkgs { + tx, err := pkg.Load() + if err != nil { + return nil, fmt.Errorf("unable to load packages: %w", err) + } + txs = append(txs, tx...) + + } + return txs, nil +} + +type PackagePath struct { + Creator bft.Address + Deposit std.Coins + Fee std.Fee + Path string +} + +func (p PackagePath) Load() ([]std.Tx, error) { + if p.Creator.IsZero() { + return nil, errors.New("empty creator address") + } + + if p.Path == "" { + return nil, errors.New("empty package path") + } + + // list all packages from target path + pkgs, err := gnomod.ListPkgs(p.Path) + if err != nil { + return nil, fmt.Errorf("listing gno packages: %w", err) + } + + // Sort packages by dependencies. + sortedPkgs, err := pkgs.Sort() + if err != nil { + return nil, fmt.Errorf("sorting packages: %w", err) + } + + // Filter out draft packages. + nonDraftPkgs := sortedPkgs.GetNonDraftPkgs() + txs := []std.Tx{} + for _, pkg := range nonDraftPkgs { + // Open files in directory as MemPackage. + memPkg := gno.ReadMemPackage(pkg.Dir, pkg.Name) + + // Create transaction + tx := std.Tx{ + Fee: p.Fee, + Msgs: []std.Msg{ + vmm.MsgAddPackage{ + Creator: p.Creator, + Package: memPkg, + Deposit: p.Deposit, + }, + }, + } + + tx.Signatures = make([]std.Signature, len(tx.GetSigners())) + txs = append(txs, tx) + } + + return txs, nil +} diff --git a/gno.land/pkg/integration/node_inmemory.go b/gno.land/pkg/integration/node_inmemory.go new file mode 100644 index 00000000000..01ef25ea061 --- /dev/null +++ b/gno.land/pkg/integration/node_inmemory.go @@ -0,0 +1,5 @@ +package integration + +import "github.com/gnolang/gno/tm2/pkg/log" + +} diff --git a/gno.land/pkg/gnoland/inmemory_test.go b/gno.land/pkg/integration/node_test.go similarity index 76% rename from gno.land/pkg/gnoland/inmemory_test.go rename to gno.land/pkg/integration/node_test.go index 9c2b79dab7d..b327117c897 100644 --- a/gno.land/pkg/gnoland/inmemory_test.go +++ b/gno.land/pkg/integration/node_test.go @@ -1,4 +1,4 @@ -package gnoland +package integration import ( "testing" @@ -10,7 +10,7 @@ import ( func TestNewInMemory(t *testing.T) { logger := log.TestingLogger() - node, err := NewInMemory(logger, InMemoryConfig{}) + node, err := NewNode(logger, NodeConfig{}) require.NoError(t, err) require.NotNil(t, node) } diff --git a/gno.land/pkg/integration/testing_integration.go b/gno.land/pkg/integration/testing_integration.go index f0a696ddd85..aa653f1db66 100644 --- a/gno.land/pkg/integration/testing_integration.go +++ b/gno.land/pkg/integration/testing_integration.go @@ -13,13 +13,17 @@ import ( "time" "github.com/gnolang/gno/gno.land/pkg/gnoland" + abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" + "github.com/gnolang/gno/tm2/pkg/bft/config" "github.com/gnolang/gno/tm2/pkg/bft/node" "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/crypto/keys" "github.com/gnolang/gno/tm2/pkg/crypto/keys/client" "github.com/gnolang/gno/tm2/pkg/events" "github.com/gnolang/gno/tm2/pkg/log" + "github.com/gnolang/gno/tm2/pkg/std" "github.com/rogpeppe/go-internal/testscript" ) @@ -51,14 +55,14 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { // `gnoRootDir` should point to the local location of the gno repository. // It serves as the gno equivalent of GOROOT. - gnoRootDir := gnoland.GuessGnoRootDir() + gnoRootDir := gnoland.MustGuessGnoRootDir() // `gnoHomeDir` should be the local directory where gnokey stores keys. gnoHomeDir := filepath.Join(tmpdir, "gno") // `gnoDataDir` should refer to the local location where the gnoland node // stores its configuration and data. - gnoDataDir := filepath.Join(tmpdir, "data") + // gnoDataDir := filepath.Join(tmpdir, "data") // Testscripts run concurrently by default, so we need to be prepared for that. var muNodes sync.Mutex @@ -115,9 +119,8 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { logger = getTestingLogger(ts, logname) } - dataDir := filepath.Join(gnoDataDir, sid) var node *node.Node - if node, err = execTestingGnoland(t, logger, dataDir, gnoRootDir, args); err == nil { + if node, err = execInMemoryGnoland(logger, gnoRootDir); err == nil { nodes[sid] = &testNode{ Node: node, logger: logger, @@ -139,7 +142,7 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { // Add default environements. ts.Setenv("RPC_ADDR", laddr) - ts.Setenv("GNODATA", gnoDataDir) + // ts.Setenv("GNODATA", gnoDataDir) const listenerID = "testing_listener" @@ -178,7 +181,7 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { // Unset gnoland environements. ts.Setenv("RPC_ADDR", "") - ts.Setenv("GNODATA", "") + // ts.Setenv("GNODATA", "") fmt.Fprintln(ts.Stdout(), "node stopped successfully") } default: @@ -280,3 +283,69 @@ func getTestingLogger(ts *testscript.TestScript, logname string) log.Logger { ts.Logf("starting logger: %q", path) return logger } + +func execInMemoryGnoland(logger log.Logger, gnoroot string) (*node.Node, error) { + txsFile := filepath.Join(gnoroot, "gno.land", "genesis", "genesis_txs.txt") + balanceFile := filepath.Join(gnoroot, "gno.land", "genesis", "genesis_balances.txt") + exampleDir := filepath.Join(gnoroot, "examples") + + genesisBalances, err := gnoland.LoadGenesisBalancesFile(balanceFile) + if err != nil { + return nil, fmt.Errorf("unableo to laod genesis balance file: %w", err) + } + + bftconfig := config.TestConfig().SetRootDir(gnoroot) + bftconfig.Consensus.CreateEmptyBlocks = true + bftconfig.Consensus.CreateEmptyBlocksInterval = time.Duration(0) + bftconfig.RPC.ListenAddress = "tcp://127.0.0.1:0" + bftconfig.P2P.ListenAddress = "tcp://127.0.0.1:0" + + // NOTE: we dont care about giving a correct address here, as it's only visual + // XXX: do we care loading this file ? + genesisTXs, err := gnoland.LoadGenesisTxsFile(txsFile, bftconfig.ChainID(), bftconfig.RPC.ListenAddress) + if err != nil { + return nil, fmt.Errorf("unableo to laod genesis balance file: %w", err) + } + + // Load example packages + pkgs := PackagePath{ + Creator: crypto.MustAddressFromString(test1Addr), + Fee: std.NewFee(50000, std.MustParseCoin("1000000ugnot")), + Path: exampleDir, + } + + config := NodeConfig{ + Balances: genesisBalances, + GenesisTXs: genesisTXs, + BFTConfig: bftconfig, + Packages: []PackagePath{pkgs}, + } + + // XXX: make this configurable + config.ConsensusParams.Block = &abci.BlockParams{ + MaxTxBytes: 1000000, // 1MB, + MaxDataBytes: 2000000, // 2MB, + MaxGas: 10000000, // 10M gas + TimeIotaMS: 100, // 100ms + } + + node, err := NewNode(logger, config) + if err != nil { + return nil, fmt.Errorf("unable to start the node: %w", err) + } + + return node, node.Start() +} + +func tsValidateError(ts *testscript.TestScript, cmd string, neg bool, err error) { + if err != nil { + fmt.Fprintf(ts.Stderr(), "%q error: %v\n", cmd, err) + if !neg { + ts.Fatalf("unexpected %q command failure: %s", cmd, err) + } + } else { + if neg { + ts.Fatalf("unexpected %s command success", cmd) + } + } +} From 9077ca12a8a392f1bfe52df809263bf16994d420 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 12 Oct 2023 17:35:52 +0200 Subject: [PATCH 04/38] chore: cleanup Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/pkg/integration/node.go | 48 +++++++++++------------ gno.land/pkg/integration/node_inmemory.go | 5 --- 2 files changed, 24 insertions(+), 29 deletions(-) delete mode 100644 gno.land/pkg/integration/node_inmemory.go diff --git a/gno.land/pkg/integration/node.go b/gno.land/pkg/integration/node.go index a0ee63a0780..527b227f8e2 100644 --- a/gno.land/pkg/integration/node.go +++ b/gno.land/pkg/integration/node.go @@ -33,23 +33,23 @@ type NodeConfig struct { GenesisMaxVMCycles int64 } -func NewNode(logger log.Logger, icfg NodeConfig) (*node.Node, error) { - bftconfig := icfg.BFTConfig +func NewNode(logger log.Logger, cfg NodeConfig) (*node.Node, error) { + // Setup setup testing config { - // Setup setup testing config - if bftconfig == nil { - bftconfig = config.TestConfig() - bftconfig.RPC.ListenAddress = "tcp://127.0.0.1:0" - bftconfig.P2P.ListenAddress = "tcp://127.0.0.1:0" + if cfg.BFTConfig == nil { + cfg.BFTConfig = config.TestConfig() + cfg.BFTConfig.RPC.ListenAddress = "tcp://127.0.0.1:0" + cfg.BFTConfig.P2P.ListenAddress = "tcp://127.0.0.1:0" } // XXX: we need to get ride of this, for now needed because of stdlib - if bftconfig.RootDir == "" { + if cfg.BFTConfig.RootDir == "" { gnoRootDir := gnoland.MustGuessGnoRootDir() - bftconfig.SetRootDir(gnoRootDir) + cfg.BFTConfig.SetRootDir(gnoRootDir) } } + // generate node identity nodekey := &p2p.NodeKey{PrivKey: ed25519.GenPrivKey()} priv := bft.NewMockPVWithParams(nodekey.PrivKey, false, false) @@ -60,7 +60,7 @@ func NewNode(logger log.Logger, icfg NodeConfig) (*node.Node, error) { gen.GenesisTime = time.Now() // cfg.chainID = "tendermint_test" - gen.ChainID = bftconfig.ChainID() + gen.ChainID = cfg.BFTConfig.ChainID() // XXX(gfanton): Do we need some default here ? // if icfg.ConsensusParams.Block == nil { @@ -71,7 +71,7 @@ func NewNode(logger log.Logger, icfg NodeConfig) (*node.Node, error) { // TimeIotaMS: 100, // 100ms // } // } - gen.ConsensusParams = icfg.ConsensusParams + gen.ConsensusParams = cfg.ConsensusParams pk := priv.GetPubKey() @@ -85,43 +85,43 @@ func NewNode(logger log.Logger, icfg NodeConfig) (*node.Node, error) { }, } - for _, validator := range icfg.GenesisValidator { + for _, validator := range cfg.GenesisValidator { gen.Validators = append(gen.Validators, validator) } } // XXX: maybe let the user do this manually and pass it to genesisTXs - txs, err := LoadPackages(icfg.Packages) + txs, err := LoadPackages(cfg.Packages) if err != nil { return nil, fmt.Errorf("uanble to load genesis packages: %w", err) } - txs = append(txs, icfg.GenesisTXs...) + txs = append(txs, cfg.GenesisTXs...) gen.AppState = gnoland.GnoGenesisState{ - Balances: icfg.Balances, + Balances: cfg.Balances, Txs: txs, } gnoApp, err := gnoland.NewAppWithOptions(&gnoland.AppOptions{ Logger: logger, - GnoRootDir: bftconfig.RootDir, - SkipFailingGenesisTxs: icfg.SkipFailingGenesisTxs, - MaxCycles: icfg.GenesisMaxVMCycles, + GnoRootDir: cfg.BFTConfig.RootDir, + SkipFailingGenesisTxs: cfg.SkipFailingGenesisTxs, + MaxCycles: cfg.GenesisMaxVMCycles, DB: db.NewMemDB(), }) if err != nil { return nil, fmt.Errorf("error in creating new app: %w", err) } - bftconfig.LocalApp = gnoApp + cfg.BFTConfig.LocalApp = gnoApp // Get app client creator. appClientCreator := proxy.DefaultClientCreator( - bftconfig.LocalApp, - bftconfig.ProxyApp, - bftconfig.ABCI, - bftconfig.DBDir(), + cfg.BFTConfig.LocalApp, + cfg.BFTConfig.ProxyApp, + cfg.BFTConfig.ABCI, + cfg.BFTConfig.DBDir(), ) // Create genesis factory. @@ -129,7 +129,7 @@ func NewNode(logger log.Logger, icfg NodeConfig) (*node.Node, error) { return gen, nil } - return node.NewNode(bftconfig, + return node.NewNode(cfg.BFTConfig, priv, nodekey, appClientCreator, genProvider, diff --git a/gno.land/pkg/integration/node_inmemory.go b/gno.land/pkg/integration/node_inmemory.go deleted file mode 100644 index 01ef25ea061..00000000000 --- a/gno.land/pkg/integration/node_inmemory.go +++ /dev/null @@ -1,5 +0,0 @@ -package integration - -import "github.com/gnolang/gno/tm2/pkg/log" - -} From 3a3133c214584d4a4b96d66a38d7c1cc986106ec Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 12 Oct 2023 17:44:33 +0200 Subject: [PATCH 05/38] wip: gnoweb Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/cmd/gnoweb/main_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/gno.land/cmd/gnoweb/main_test.go b/gno.land/cmd/gnoweb/main_test.go index 579e1bcd06b..4e0175deea3 100644 --- a/gno.land/cmd/gnoweb/main_test.go +++ b/gno.land/cmd/gnoweb/main_test.go @@ -8,6 +8,8 @@ import ( "strings" "testing" + "github.com/gnolang/gno/gno.land/pkg/integration" + "github.com/gnolang/gno/tm2/pkg/log" "github.com/gotuna/gotuna/test/assert" ) @@ -40,6 +42,13 @@ func TestRoutes(t *testing.T) { } else { panic("os.Getwd() -> err: " + err.Error()) } + + node := integration.TestingInMemoryNode(t, log.NewNopLogger(), nil) + defer node.Stop() + + // XXX: this is ugly :( + flags.remoteAddr = node.Config().RPC.ListenAddress + app := makeApp() for _, r := range routes { From 06ecc62893ef67dd47a731d6b93b38fec67faac4 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 12 Oct 2023 17:47:55 +0200 Subject: [PATCH 06/38] chore: lint Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/pkg/gnoland/node.go | 115 ------------------------------- gno.land/pkg/integration/node.go | 2 +- 2 files changed, 1 insertion(+), 116 deletions(-) delete mode 100644 gno.land/pkg/gnoland/node.go diff --git a/gno.land/pkg/gnoland/node.go b/gno.land/pkg/gnoland/node.go deleted file mode 100644 index d9e54f35cd7..00000000000 --- a/gno.land/pkg/gnoland/node.go +++ /dev/null @@ -1,115 +0,0 @@ -package gnoland - -// func loadGenesisTxs( -// path string, -// chainID string, -// genesisRemote string, -// ) []std.Tx { -// txs := []std.Tx{} -// txsBz := osm.MustReadFile(path) -// txsLines := strings.Split(string(txsBz), "\n") -// for _, txLine := range txsLines { -// if txLine == "" { -// continue // skip empty line -// } - -// // patch the TX -// txLine = strings.ReplaceAll(txLine, "%%CHAINID%%", chainID) -// txLine = strings.ReplaceAll(txLine, "%%REMOTE%%", genesisRemote) - -// var tx std.Tx -// amino.MustUnmarshalJSON([]byte(txLine), &tx) -// txs = append(txs, tx) -// } - -// return txs -// } - -// func setupTestingGenesis(gnoDataDir string, cfg *config.Config, icfg *IntegrationConfig) error { -// genesisFilePath := filepath.Join(gnoDataDir, cfg.Genesis) -// osm.EnsureDir(filepath.Dir(genesisFilePath), 0o700) -// if !osm.FileExists(genesisFilePath) { -// genesisTxs := loadGenesisTxs(icfg.GenesisTxsFile, icfg.ChainID, icfg.GenesisRemote) -// pvPub := priv.GetPubKey() - -// gen := &bft.GenesisDoc{ -// GenesisTime: time.Now(), -// ChainID: icfg.ChainID, -// ConsensusParams: abci.ConsensusParams{ -// Block: &abci.BlockParams{ -// // TODO: update limits. -// MaxTxBytes: 1000000, // 1MB, -// MaxDataBytes: 2000000, // 2MB, -// MaxGas: 10000000, // 10M gas -// TimeIotaMS: 100, // 100ms -// }, -// }, -// Validators: []bft.GenesisValidator{ -// { -// Address: pvPub.Address(), -// PubKey: pvPub, -// Power: 10, -// Name: "testvalidator", -// }, -// }, -// } - -// // Load distribution. -// balances := loadGenesisBalances(icfg.GenesisBalancesFile) - -// // Load initial packages from examples. -// // XXX: we should be able to config this -// test1 := crypto.MustAddressFromString(test1Addr) -// txs := []std.Tx{} - -// // List initial packages to load from examples. -// // println(filepath.Join(gnoRootDir, "examples")) - -// // load genesis txs from file. -// txs = append(txs, genesisTxs...) - -// // construct genesis AppState. -// gen.AppState = GnoGenesisState{ -// Balances: balances, -// Txs: txs, -// } - -// writeGenesisFile(gen, genesisFilePath) -// } - -// return nil -// } - -// func loadGenesisBalances(path string) []string { -// // each balance is in the form: g1xxxxxxxxxxxxxxxx=100000ugnot -// balances := []string{} -// content := osm.MustReadFile(path) -// lines := strings.Split(string(content), "\n") -// for _, line := range lines { -// line = strings.TrimSpace(line) - -// // remove comments. -// line = strings.Split(line, "#")[0] -// line = strings.TrimSpace(line) - -// // skip empty lines. -// if line == "" { -// continue -// } - -// parts := strings.Split(line, "=") -// if len(parts) != 2 { -// panic("invalid genesis_balance line: " + line) -// } - -// balances = append(balances, line) -// } -// return balances -// } - -// func writeGenesisFile(gen *bft.GenesisDoc, filePath string) { -// err := gen.SaveAs(filePath) -// if err != nil { -// panic(err) -// } -// } diff --git a/gno.land/pkg/integration/node.go b/gno.land/pkg/integration/node.go index 527b227f8e2..69a013f4572 100644 --- a/gno.land/pkg/integration/node.go +++ b/gno.land/pkg/integration/node.go @@ -34,7 +34,7 @@ type NodeConfig struct { } func NewNode(logger log.Logger, cfg NodeConfig) (*node.Node, error) { - // Setup setup testing config + // Setup setup testing config if needed { if cfg.BFTConfig == nil { cfg.BFTConfig = config.TestConfig() From 84907d13144939850f524f48de7c9ed932720bc2 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 12 Oct 2023 17:55:30 +0200 Subject: [PATCH 07/38] chore: add some comments Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/pkg/gnoland/app.go | 1 + gno.land/pkg/integration/testing_integration.go | 1 + 2 files changed, 2 insertions(+) diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index 6ac13d4ff99..9a7a0f91d3b 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -199,6 +199,7 @@ func EndBlocker(vmk vm.VMKeeperI) func(ctx sdk.Context, req abci.RequestEndBlock } } +// XXX: should be removed in favor of https://github.com/gnolang/gno/pull/1233 var ( guessOnce sync.Once gnoroot string diff --git a/gno.land/pkg/integration/testing_integration.go b/gno.land/pkg/integration/testing_integration.go index aa653f1db66..035c1879169 100644 --- a/gno.land/pkg/integration/testing_integration.go +++ b/gno.land/pkg/integration/testing_integration.go @@ -144,6 +144,7 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { ts.Setenv("RPC_ADDR", laddr) // ts.Setenv("GNODATA", gnoDataDir) + // XXX: This should be replace by https://github.com/gnolang/gno/pull/1216 const listenerID = "testing_listener" // Wait for first block by waiting for `EventNewBlock` event. From 8e8bed2214e0e115ea9f55d6892f7024a8734aa0 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 12 Oct 2023 18:50:12 +0200 Subject: [PATCH 08/38] fix: gnoweb tests Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/cmd/gnoweb/main_test.go | 4 +- .../pkg/integration/testing_integration.go | 56 ++----------------- 2 files changed, 8 insertions(+), 52 deletions(-) diff --git a/gno.land/cmd/gnoweb/main_test.go b/gno.land/cmd/gnoweb/main_test.go index 4e0175deea3..4ef6e3e6dac 100644 --- a/gno.land/cmd/gnoweb/main_test.go +++ b/gno.land/cmd/gnoweb/main_test.go @@ -8,6 +8,7 @@ import ( "strings" "testing" + "github.com/gnolang/gno/gno.land/pkg/gnoland" "github.com/gnolang/gno/gno.land/pkg/integration" "github.com/gnolang/gno/tm2/pkg/log" "github.com/gotuna/gotuna/test/assert" @@ -43,7 +44,8 @@ func TestRoutes(t *testing.T) { panic("os.Getwd() -> err: " + err.Error()) } - node := integration.TestingInMemoryNode(t, log.NewNopLogger(), nil) + config := integration.DefaultTestingConfig(t, gnoland.MustGuessGnoRootDir()) + node := integration.TestingInMemoryNode(t, log.NewNopLogger(), config) defer node.Stop() // XXX: this is ugly :( diff --git a/gno.land/pkg/integration/testing_integration.go b/gno.land/pkg/integration/testing_integration.go index 035c1879169..9ddc57fc7a1 100644 --- a/gno.land/pkg/integration/testing_integration.go +++ b/gno.land/pkg/integration/testing_integration.go @@ -13,17 +13,13 @@ import ( "time" "github.com/gnolang/gno/gno.land/pkg/gnoland" - abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" - "github.com/gnolang/gno/tm2/pkg/bft/config" "github.com/gnolang/gno/tm2/pkg/bft/node" "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/commands" - "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/crypto/keys" "github.com/gnolang/gno/tm2/pkg/crypto/keys/client" "github.com/gnolang/gno/tm2/pkg/events" "github.com/gnolang/gno/tm2/pkg/log" - "github.com/gnolang/gno/tm2/pkg/std" "github.com/rogpeppe/go-internal/testscript" ) @@ -68,6 +64,8 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { var muNodes sync.Mutex nodes := map[string]*testNode{} + nodeConfig := DefaultTestingConfig(t, gnoRootDir) + updateScripts, _ := strconv.ParseBool(os.Getenv("UPDATE_SCRIPTS")) persistWorkDir, _ := strconv.ParseBool(os.Getenv("TESTWORK")) return testscript.Params{ @@ -120,7 +118,7 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { } var node *node.Node - if node, err = execInMemoryGnoland(logger, gnoRootDir); err == nil { + if node, err = execInMemoryGnoland(logger, nodeConfig); err == nil { nodes[sid] = &testNode{ Node: node, logger: logger, @@ -285,52 +283,8 @@ func getTestingLogger(ts *testscript.TestScript, logname string) log.Logger { return logger } -func execInMemoryGnoland(logger log.Logger, gnoroot string) (*node.Node, error) { - txsFile := filepath.Join(gnoroot, "gno.land", "genesis", "genesis_txs.txt") - balanceFile := filepath.Join(gnoroot, "gno.land", "genesis", "genesis_balances.txt") - exampleDir := filepath.Join(gnoroot, "examples") - - genesisBalances, err := gnoland.LoadGenesisBalancesFile(balanceFile) - if err != nil { - return nil, fmt.Errorf("unableo to laod genesis balance file: %w", err) - } - - bftconfig := config.TestConfig().SetRootDir(gnoroot) - bftconfig.Consensus.CreateEmptyBlocks = true - bftconfig.Consensus.CreateEmptyBlocksInterval = time.Duration(0) - bftconfig.RPC.ListenAddress = "tcp://127.0.0.1:0" - bftconfig.P2P.ListenAddress = "tcp://127.0.0.1:0" - - // NOTE: we dont care about giving a correct address here, as it's only visual - // XXX: do we care loading this file ? - genesisTXs, err := gnoland.LoadGenesisTxsFile(txsFile, bftconfig.ChainID(), bftconfig.RPC.ListenAddress) - if err != nil { - return nil, fmt.Errorf("unableo to laod genesis balance file: %w", err) - } - - // Load example packages - pkgs := PackagePath{ - Creator: crypto.MustAddressFromString(test1Addr), - Fee: std.NewFee(50000, std.MustParseCoin("1000000ugnot")), - Path: exampleDir, - } - - config := NodeConfig{ - Balances: genesisBalances, - GenesisTXs: genesisTXs, - BFTConfig: bftconfig, - Packages: []PackagePath{pkgs}, - } - - // XXX: make this configurable - config.ConsensusParams.Block = &abci.BlockParams{ - MaxTxBytes: 1000000, // 1MB, - MaxDataBytes: 2000000, // 2MB, - MaxGas: 10000000, // 10M gas - TimeIotaMS: 100, // 100ms - } - - node, err := NewNode(logger, config) +func execInMemoryGnoland(logger log.Logger, config *NodeConfig) (*node.Node, error) { + node, err := NewNode(logger, *config) if err != nil { return nil, fmt.Errorf("unable to start the node: %w", err) } From d5101408efd002b062466e349b11dfad33fd8418 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 12 Oct 2023 18:54:42 +0200 Subject: [PATCH 09/38] fix: enable gnoweb test on the CI Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- .github/workflows/gnoland.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gnoland.yml b/.github/workflows/gnoland.yml index 2b38f254a13..949151e42be 100644 --- a/.github/workflows/gnoland.yml +++ b/.github/workflows/gnoland.yml @@ -60,7 +60,7 @@ jobs: - _test.gnoland - _test.gnokey - _test.pkgs - #- _test.gnoweb # this test should be rewritten to run an inmemory localnode + - _test.gnoweb # this test should be rewritten to run an inmemory localnode runs-on: ubuntu-latest timeout-minutes: 15 steps: From 3221a1ca5f915151cf33e9a4536d3d35b76affba Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 12 Oct 2023 19:02:28 +0200 Subject: [PATCH 10/38] feat: add testing node Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/pkg/integration/testing_node.go | 96 ++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 gno.land/pkg/integration/testing_node.go diff --git a/gno.land/pkg/integration/testing_node.go b/gno.land/pkg/integration/testing_node.go new file mode 100644 index 00000000000..760e6201cdd --- /dev/null +++ b/gno.land/pkg/integration/testing_node.go @@ -0,0 +1,96 @@ +package integration + +import ( + "path/filepath" + "testing" + "time" + + "github.com/gnolang/gno/gno.land/pkg/gnoland" + abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" + "github.com/gnolang/gno/tm2/pkg/bft/config" + "github.com/gnolang/gno/tm2/pkg/bft/node" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/events" + "github.com/gnolang/gno/tm2/pkg/log" + "github.com/gnolang/gno/tm2/pkg/std" + "github.com/jaekwon/testify/require" +) + +func DefaultTestingConfig(t *testing.T, gnoroot string) *NodeConfig { + txsFile := filepath.Join(gnoroot, "gno.land", "genesis", "genesis_txs.txt") + balanceFile := filepath.Join(gnoroot, "gno.land", "genesis", "genesis_balances.txt") + exampleDir := filepath.Join(gnoroot, "examples") + + genesisBalances, err := gnoland.LoadGenesisBalancesFile(balanceFile) + require.NoError(t, err) + + bftconfig := config.TestConfig().SetRootDir(gnoroot) + bftconfig.Consensus.CreateEmptyBlocks = true + bftconfig.Consensus.CreateEmptyBlocksInterval = time.Duration(0) + bftconfig.RPC.ListenAddress = "tcp://127.0.0.1:0" + bftconfig.P2P.ListenAddress = "tcp://127.0.0.1:0" + + // NOTE: we dont care about giving a correct address here, as it's only visual + // XXX: do we care loading this file ? + genesisTXs, err := gnoland.LoadGenesisTxsFile(txsFile, bftconfig.ChainID(), bftconfig.RPC.ListenAddress) + require.NoError(t, err) + + // Load example packages + pkgs := PackagePath{ + Creator: crypto.MustAddressFromString(test1Addr), + Fee: std.NewFee(50000, std.MustParseCoin("1000000ugnot")), + Path: exampleDir, + } + + config := NodeConfig{ + Balances: genesisBalances, + GenesisTXs: genesisTXs, + BFTConfig: bftconfig, + Packages: []PackagePath{pkgs}, + } + + config.ConsensusParams.Block = &abci.BlockParams{ + MaxTxBytes: 1000000, // 1MB, + MaxDataBytes: 2000000, // 2MB, + MaxGas: 10000000, // 10M gas + TimeIotaMS: 100, // 100ms + } + + return &config +} + +// Should return an already starting node +func TestingInMemoryNode(t *testing.T, logger log.Logger, config *NodeConfig) *node.Node { + node, err := NewNode(logger, *config) + require.NoError(t, err) + + err = node.Start() + require.NoError(t, err) + + // XXX: This should be replace by https://github.com/gnolang/gno/pull/1216 + const listenerID = "testing_listener" + + // Wait for first block by waiting for `EventNewBlock` event. + nb := make(chan struct{}, 1) + node.EventSwitch().AddListener(listenerID, func(ev events.Event) { + if _, ok := ev.(types.EventNewBlock); ok { + select { + case nb <- struct{}{}: + default: + } + } + }) + + if node.BlockStore().Height() == 0 { + select { + case <-nb: // ok + case <-time.After(time.Second * 6): + t.Fatal("timeout while waiting for the node to start") + } + } + + node.EventSwitch().RemoveListener(listenerID) + + return node +} From 92de25630c913d1801ada95abcc04624dff71ea8 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 12 Oct 2023 20:33:38 +0200 Subject: [PATCH 11/38] chore: remove useless comment Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- .github/workflows/gnoland.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gnoland.yml b/.github/workflows/gnoland.yml index 949151e42be..d305bed2dcd 100644 --- a/.github/workflows/gnoland.yml +++ b/.github/workflows/gnoland.yml @@ -60,7 +60,7 @@ jobs: - _test.gnoland - _test.gnokey - _test.pkgs - - _test.gnoweb # this test should be rewritten to run an inmemory localnode + - _test.gnoweb runs-on: ubuntu-latest timeout-minutes: 15 steps: From 2003899c919c596f42f8077ce1ec1cfbf55bc07a Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 12 Oct 2023 21:04:46 +0200 Subject: [PATCH 12/38] feat: add testing helper Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/cmd/gnoweb/main_test.go | 4 +- .../pkg/integration/testing_integration.go | 2 +- gno.land/pkg/integration/testing_node.go | 124 +++++++++++------- 3 files changed, 82 insertions(+), 48 deletions(-) diff --git a/gno.land/cmd/gnoweb/main_test.go b/gno.land/cmd/gnoweb/main_test.go index 4ef6e3e6dac..66e35f28b5b 100644 --- a/gno.land/cmd/gnoweb/main_test.go +++ b/gno.land/cmd/gnoweb/main_test.go @@ -44,8 +44,8 @@ func TestRoutes(t *testing.T) { panic("os.Getwd() -> err: " + err.Error()) } - config := integration.DefaultTestingConfig(t, gnoland.MustGuessGnoRootDir()) - node := integration.TestingInMemoryNode(t, log.NewNopLogger(), config) + config := integration.DefaultTestingNodeConfig(t, gnoland.MustGuessGnoRootDir()) + node, remoteAddr := integration.TestingInMemoryNode(t, log.NewNopLogger(), config) defer node.Stop() // XXX: this is ugly :( diff --git a/gno.land/pkg/integration/testing_integration.go b/gno.land/pkg/integration/testing_integration.go index 9ddc57fc7a1..1092230cf5e 100644 --- a/gno.land/pkg/integration/testing_integration.go +++ b/gno.land/pkg/integration/testing_integration.go @@ -64,7 +64,7 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { var muNodes sync.Mutex nodes := map[string]*testNode{} - nodeConfig := DefaultTestingConfig(t, gnoRootDir) + nodeConfig := DefaultTestingNodeConfig(t, gnoRootDir) updateScripts, _ := strconv.ParseBool(os.Getenv("UPDATE_SCRIPTS")) persistWorkDir, _ := strconv.ParseBool(os.Getenv("TESTWORK")) diff --git a/gno.land/pkg/integration/testing_node.go b/gno.land/pkg/integration/testing_node.go index 760e6201cdd..8cfe5171e7c 100644 --- a/gno.land/pkg/integration/testing_node.go +++ b/gno.land/pkg/integration/testing_node.go @@ -10,6 +10,7 @@ import ( "github.com/gnolang/gno/tm2/pkg/bft/config" "github.com/gnolang/gno/tm2/pkg/bft/node" "github.com/gnolang/gno/tm2/pkg/bft/types" + bft "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/events" "github.com/gnolang/gno/tm2/pkg/log" @@ -17,51 +18,8 @@ import ( "github.com/jaekwon/testify/require" ) -func DefaultTestingConfig(t *testing.T, gnoroot string) *NodeConfig { - txsFile := filepath.Join(gnoroot, "gno.land", "genesis", "genesis_txs.txt") - balanceFile := filepath.Join(gnoroot, "gno.land", "genesis", "genesis_balances.txt") - exampleDir := filepath.Join(gnoroot, "examples") - - genesisBalances, err := gnoland.LoadGenesisBalancesFile(balanceFile) - require.NoError(t, err) - - bftconfig := config.TestConfig().SetRootDir(gnoroot) - bftconfig.Consensus.CreateEmptyBlocks = true - bftconfig.Consensus.CreateEmptyBlocksInterval = time.Duration(0) - bftconfig.RPC.ListenAddress = "tcp://127.0.0.1:0" - bftconfig.P2P.ListenAddress = "tcp://127.0.0.1:0" - - // NOTE: we dont care about giving a correct address here, as it's only visual - // XXX: do we care loading this file ? - genesisTXs, err := gnoland.LoadGenesisTxsFile(txsFile, bftconfig.ChainID(), bftconfig.RPC.ListenAddress) - require.NoError(t, err) - - // Load example packages - pkgs := PackagePath{ - Creator: crypto.MustAddressFromString(test1Addr), - Fee: std.NewFee(50000, std.MustParseCoin("1000000ugnot")), - Path: exampleDir, - } - - config := NodeConfig{ - Balances: genesisBalances, - GenesisTXs: genesisTXs, - BFTConfig: bftconfig, - Packages: []PackagePath{pkgs}, - } - - config.ConsensusParams.Block = &abci.BlockParams{ - MaxTxBytes: 1000000, // 1MB, - MaxDataBytes: 2000000, // 2MB, - MaxGas: 10000000, // 10M gas - TimeIotaMS: 100, // 100ms - } - - return &config -} - // Should return an already starting node -func TestingInMemoryNode(t *testing.T, logger log.Logger, config *NodeConfig) *node.Node { +func TestingInMemoryNode(t *testing.T, logger log.Logger, config *NodeConfig) (*node.Node, string) { node, err := NewNode(logger, *config) require.NoError(t, err) @@ -92,5 +50,81 @@ func TestingInMemoryNode(t *testing.T, logger log.Logger, config *NodeConfig) *n node.EventSwitch().RemoveListener(listenerID) - return node + return node, node.Config().RPC.ListenAddress +} + +func DefaultTestingNodeConfig(t *testing.T, gnoroot string) *NodeConfig { + t.Helper() + + bftconfig := DefaultTestingBFTConfig(t, gnoroot) + + return &NodeConfig{ + Balances: LoadDefaultGenesisBalanceFile(t, gnoroot), + GenesisTXs: LoadDefaultGenesisTXsFile(t, bftconfig.ChainID(), gnoroot), + BFTConfig: bftconfig, + Packages: LoadDefaultPackages(t, crypto.MustAddressFromString(test1Addr), gnoroot), + } +} + +func LoadDefaultPackages(t *testing.T, creator bft.Address, gnoroot string) []PackagePath { + t.Helper() + + exampleDir := filepath.Join(gnoroot, "examples") + + pkgs := PackagePath{ + // Creator: crypto.MustAddressFromString(test1Addr), + Creator: creator, + Fee: std.NewFee(50000, std.MustParseCoin("1000000ugnot")), + Path: exampleDir, + } + + return []PackagePath{pkgs} +} + +func LoadDefaultGenesisBalanceFile(t *testing.T, gnoroot string) []gnoland.Balance { + t.Helper() + + balanceFile := filepath.Join(gnoroot, "gno.land", "genesis", "genesis_balances.txt") + + genesisBalances, err := gnoland.LoadGenesisBalancesFile(balanceFile) + require.NoError(t, err) + + return genesisBalances +} + +func LoadDefaultGenesisTXsFile(t *testing.T, chainid string, gnoroot string) []std.Tx { + t.Helper() + + txsFile := filepath.Join(gnoroot, "gno.land", "genesis", "genesis_txs.txt") + + // NOTE: we dont care about giving a correct address here, as it's only visual + // XXX: do we care loading this file ? + genesisTXs, err := gnoland.LoadGenesisTxsFile(txsFile, chainid, "https://127.0.0.1:26657") + require.NoError(t, err) + + return genesisTXs +} + +func DefaultConsensusParams(t *testing.T, gnoroot string) *abci.ConsensusParams { + t.Helper() + + return &abci.ConsensusParams{ + Block: &abci.BlockParams{ + MaxTxBytes: 1000000, // 1MB, + MaxDataBytes: 2000000, // 2MB, + MaxGas: 10000000, // 10M gas + TimeIotaMS: 100, // 100ms + }, + } +} + +func DefaultTestingBFTConfig(t *testing.T, gnoroot string) *config.Config { + t.Helper() + + bftconfig := config.TestConfig().SetRootDir(gnoroot) + bftconfig.Consensus.CreateEmptyBlocks = true + bftconfig.Consensus.CreateEmptyBlocksInterval = time.Duration(0) + bftconfig.RPC.ListenAddress = "tcp://127.0.0.1:0" + bftconfig.P2P.ListenAddress = "tcp://127.0.0.1:0" + return bftconfig } From e0b59fdff005a2e17cdd40eb908e993f163a854c Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 12 Oct 2023 21:12:10 +0200 Subject: [PATCH 13/38] chore: lint Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/cmd/gnoland/root.go | 3 +-- gno.land/cmd/gnoweb/main_test.go | 4 ++-- gno.land/pkg/gnoland/app.go | 3 ++- gno.land/pkg/gnoland/types.go | 16 +++------------- gno.land/pkg/integration/node.go | 10 ++++------ gno.land/pkg/integration/testing_node.go | 9 +++++---- 6 files changed, 17 insertions(+), 28 deletions(-) diff --git a/gno.land/cmd/gnoland/root.go b/gno.land/cmd/gnoland/root.go index cf2a6252478..5b2cbe0e4fe 100644 --- a/gno.land/cmd/gnoland/root.go +++ b/gno.land/cmd/gnoland/root.go @@ -11,8 +11,7 @@ import ( ) func main() { - io := commands.NewDefaultIO() - cmd := newRootCmd(io) + cmd := newRootCmd(commands.NewDefaultIO()) if err := cmd.ParseAndRun(context.Background(), os.Args[1:]); err != nil { _, _ = fmt.Fprintf(os.Stderr, "%+v\n", err) diff --git a/gno.land/cmd/gnoweb/main_test.go b/gno.land/cmd/gnoweb/main_test.go index 66e35f28b5b..d395c442fbf 100644 --- a/gno.land/cmd/gnoweb/main_test.go +++ b/gno.land/cmd/gnoweb/main_test.go @@ -48,8 +48,8 @@ func TestRoutes(t *testing.T) { node, remoteAddr := integration.TestingInMemoryNode(t, log.NewNopLogger(), config) defer node.Stop() - // XXX: this is ugly :( - flags.remoteAddr = node.Config().RPC.ListenAddress + // set the `remoteAddr` of the client to the listening address of the node, which is randomly assigned. + flags.remoteAddr = remoteAddr app := makeApp() diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index 9a7a0f91d3b..757c5f2248c 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -199,7 +199,8 @@ func EndBlocker(vmk vm.VMKeeperI) func(ctx sdk.Context, req abci.RequestEndBlock } } -// XXX: should be removed in favor of https://github.com/gnolang/gno/pull/1233 +// XXX: all the method bellow should be removed in favor of +// https://github.com/gnolang/gno/pull/1233 var ( guessOnce sync.Once gnoroot string diff --git a/gno.land/pkg/gnoland/types.go b/gno.land/pkg/gnoland/types.go index 721f2c726f2..7dba82bc6a3 100644 --- a/gno.land/pkg/gnoland/types.go +++ b/gno.land/pkg/gnoland/types.go @@ -1,7 +1,6 @@ package gnoland import ( - "errors" "fmt" "strings" @@ -29,9 +28,9 @@ type Balance struct { } func (b *Balance) Parse(line string) error { - parts := strings.Split(strings.TrimSpace(line), "=") //
= + parts := strings.Split(strings.TrimSpace(line), "=") //
= if len(parts) != 2 { - return errors.New("invalid genesis_balance line: " + line) + return fmt.Errorf("invalid balance line: %q", line) } var err error @@ -52,6 +51,7 @@ func (b *Balance) Parse(line string) error { func (b *Balance) UnmarshalJSON(data []byte) error { return b.Parse(string(data)) } + func (b *Balance) Marshaljson() ([]byte, error) { return []byte(b.String()), nil } @@ -59,13 +59,3 @@ func (b *Balance) Marshaljson() ([]byte, error) { func (b Balance) String() string { return fmt.Sprintf("%s=%s", b.Address.String(), b.Value.String()) } - -// type Balances []Balance - -// func (bs Balances) Strings() []string { -// bss := make([]string, len(bs)) -// for i, balance := range bs { -// bss[i] = balance.String() -// } -// return bss -// } diff --git a/gno.land/pkg/integration/node.go b/gno.land/pkg/integration/node.go index 69a013f4572..10d44db2223 100644 --- a/gno.land/pkg/integration/node.go +++ b/gno.land/pkg/integration/node.go @@ -13,7 +13,6 @@ import ( "github.com/gnolang/gno/tm2/pkg/bft/config" "github.com/gnolang/gno/tm2/pkg/bft/node" "github.com/gnolang/gno/tm2/pkg/bft/proxy" - "github.com/gnolang/gno/tm2/pkg/bft/types" bft "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/crypto/ed25519" "github.com/gnolang/gno/tm2/pkg/db" @@ -36,10 +35,11 @@ type NodeConfig struct { func NewNode(logger log.Logger, cfg NodeConfig) (*node.Node, error) { // Setup setup testing config if needed { + const defaultListner = "tcp://127.0.0.1:0" if cfg.BFTConfig == nil { cfg.BFTConfig = config.TestConfig() - cfg.BFTConfig.RPC.ListenAddress = "tcp://127.0.0.1:0" - cfg.BFTConfig.P2P.ListenAddress = "tcp://127.0.0.1:0" + cfg.BFTConfig.RPC.ListenAddress = defaultListner + cfg.BFTConfig.P2P.ListenAddress = defaultListner } // XXX: we need to get ride of this, for now needed because of stdlib @@ -56,7 +56,6 @@ func NewNode(logger log.Logger, cfg NodeConfig) (*node.Node, error) { // Setup geeneis gen := &bft.GenesisDoc{} { - gen.GenesisTime = time.Now() // cfg.chainID = "tendermint_test" @@ -125,7 +124,7 @@ func NewNode(logger log.Logger, cfg NodeConfig) (*node.Node, error) { ) // Create genesis factory. - genProvider := func() (*types.GenesisDoc, error) { + genProvider := func() (*bft.GenesisDoc, error) { return gen, nil } @@ -146,7 +145,6 @@ func LoadPackages(pkgs []PackagePath) ([]std.Tx, error) { return nil, fmt.Errorf("unable to load packages: %w", err) } txs = append(txs, tx...) - } return txs, nil } diff --git a/gno.land/pkg/integration/testing_node.go b/gno.land/pkg/integration/testing_node.go index 8cfe5171e7c..ea3ab667f91 100644 --- a/gno.land/pkg/integration/testing_node.go +++ b/gno.land/pkg/integration/testing_node.go @@ -9,7 +9,6 @@ import ( abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" "github.com/gnolang/gno/tm2/pkg/bft/config" "github.com/gnolang/gno/tm2/pkg/bft/node" - "github.com/gnolang/gno/tm2/pkg/bft/types" bft "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/events" @@ -32,7 +31,7 @@ func TestingInMemoryNode(t *testing.T, logger log.Logger, config *NodeConfig) (* // Wait for first block by waiting for `EventNewBlock` event. nb := make(chan struct{}, 1) node.EventSwitch().AddListener(listenerID, func(ev events.Event) { - if _, ok := ev.(types.EventNewBlock); ok { + if _, ok := ev.(bft.EventNewBlock); ok { select { case nb <- struct{}{}: default: @@ -121,10 +120,12 @@ func DefaultConsensusParams(t *testing.T, gnoroot string) *abci.ConsensusParams func DefaultTestingBFTConfig(t *testing.T, gnoroot string) *config.Config { t.Helper() + const defaultListner = "tcp://127.0.0.1:0" + bftconfig := config.TestConfig().SetRootDir(gnoroot) bftconfig.Consensus.CreateEmptyBlocks = true bftconfig.Consensus.CreateEmptyBlocksInterval = time.Duration(0) - bftconfig.RPC.ListenAddress = "tcp://127.0.0.1:0" - bftconfig.P2P.ListenAddress = "tcp://127.0.0.1:0" + bftconfig.RPC.ListenAddress = defaultListner + bftconfig.P2P.ListenAddress = defaultListner return bftconfig } From 6d761d731da65e9d22b05f3eb46951712d05566f Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Fri, 13 Oct 2023 12:56:06 +0200 Subject: [PATCH 14/38] wip: testing integration inmemory node Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/cmd/gnoland/start.go | 214 +++++++----------- gno.land/pkg/gnoland/types.go | 58 +++++ gno.land/pkg/integration/node.go | 121 +++------- gno.land/pkg/integration/node_test.go | 16 -- .../pkg/integration/testing_integration.go | 96 +++----- gno.land/pkg/integration/testing_node.go | 49 ++-- 6 files changed, 217 insertions(+), 337 deletions(-) delete mode 100644 gno.land/pkg/integration/node_test.go diff --git a/gno.land/cmd/gnoland/start.go b/gno.land/cmd/gnoland/start.go index b2134d86ea9..6564133eedc 100644 --- a/gno.land/cmd/gnoland/start.go +++ b/gno.land/cmd/gnoland/start.go @@ -5,14 +5,9 @@ import ( "flag" "fmt" "path/filepath" - "strings" "time" "github.com/gnolang/gno/gno.land/pkg/gnoland" - vmm "github.com/gnolang/gno/gno.land/pkg/sdk/vm" - gno "github.com/gnolang/gno/gnovm/pkg/gnolang" - "github.com/gnolang/gno/gnovm/pkg/gnomod" - "github.com/gnolang/gno/tm2/pkg/amino" abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" "github.com/gnolang/gno/tm2/pkg/bft/config" "github.com/gnolang/gno/tm2/pkg/bft/node" @@ -26,13 +21,14 @@ import ( ) type startCfg struct { + gnoRootDir string skipFailingGenesisTxs bool skipStart bool genesisBalancesFile string genesisTxsFile string chainID string genesisRemote string - rootDir string + dataDir string genesisMaxVMCycles int64 config string } @@ -54,6 +50,10 @@ func newStartCmd(io *commands.IO) *commands.Command { } func (c *startCfg) RegisterFlags(fs *flag.FlagSet) { + gnoroot := gnoland.MustGuessGnoRootDir() + defaultGenesisBalancesFile := filepath.Join(gnoroot, "gno.land/genesis/genesis_balances.txt") + defaultGenesisTxsFile := filepath.Join(gnoroot, "gno.land/genesis/genesis_txs.txt") + fs.BoolVar( &c.skipFailingGenesisTxs, "skip-failing-genesis-txs", @@ -71,14 +71,14 @@ func (c *startCfg) RegisterFlags(fs *flag.FlagSet) { fs.StringVar( &c.genesisBalancesFile, "genesis-balances-file", - "./genesis/genesis_balances.txt", + defaultGenesisBalancesFile, "initial distribution file", ) fs.StringVar( &c.genesisTxsFile, "genesis-txs-file", - "./genesis/genesis_txs.txt", + defaultGenesisTxsFile, "initial txs to replay", ) @@ -90,8 +90,16 @@ func (c *startCfg) RegisterFlags(fs *flag.FlagSet) { ) fs.StringVar( - &c.rootDir, - "root-dir", + &c.gnoRootDir, + "gnoroot-dir", + gnoroot, + "the root directory of the gno repository", + ) + + // XXX: Use home directory for this + fs.StringVar( + &c.dataDir, + "data-dir", "testdir", "directory for config and data", ) @@ -116,37 +124,43 @@ func (c *startCfg) RegisterFlags(fs *flag.FlagSet) { "", "config file (optional)", ) + + // XXX(deprecated): use data-dir instead + fs.StringVar( + &c.dataDir, + "root-dir", + "testdir", + "deprecated: use data-dir instead - directory for config and data", + ) } func execStart(c *startCfg, args []string, io *commands.IO) error { logger := log.NewTMLogger(log.NewSyncWriter(io.Out)) - rootDir := c.rootDir + dataDir := c.dataDir - cfg := config.LoadOrMakeConfigWithOptions(rootDir, func(cfg *config.Config) { + cfg := config.LoadOrMakeConfigWithOptions(dataDir, func(cfg *config.Config) { cfg.Consensus.CreateEmptyBlocks = true cfg.Consensus.CreateEmptyBlocksInterval = 0 * time.Second }) - // create priv validator first. - // need it to generate genesis.json - newPrivValKey := cfg.PrivValidatorKeyFile() - newPrivValState := cfg.PrivValidatorStateFile() - priv := privval.LoadOrGenFilePV(newPrivValKey, newPrivValState) - - // write genesis file if missing. - genesisFilePath := filepath.Join(rootDir, cfg.Genesis) + // Write genesis file if missing. + genesisFilePath := filepath.Join(dataDir, cfg.Genesis) if !osm.FileExists(genesisFilePath) { - genDoc := makeGenesisDoc( - priv.GetPubKey(), - c.chainID, - c.genesisBalancesFile, - loadGenesisTxs(c.genesisTxsFile, c.chainID, c.genesisRemote), - ) - writeGenesisFile(genDoc, genesisFilePath) + // Create priv validator first. + // Need it to generate genesis.json + newPrivValKey := cfg.PrivValidatorKeyFile() + newPrivValState := cfg.PrivValidatorStateFile() + priv := privval.LoadOrGenFilePV(newPrivValKey, newPrivValState) + pk := priv.GetPubKey() + + // Generate genesis.json file + if err := generateGenesisFile(genesisFilePath, pk, c); err != nil { + return fmt.Errorf("unable to generate genesis file: %w", err) + } } - // create application and node. - gnoApp, err := gnoland.NewApp(rootDir, c.skipFailingGenesisTxs, logger, c.genesisMaxVMCycles) + // Create application and node. + gnoApp, err := gnoland.NewApp(dataDir, c.skipFailingGenesisTxs, logger, c.genesisMaxVMCycles) if err != nil { return fmt.Errorf("error in creating new app: %w", err) } @@ -158,11 +172,10 @@ func execStart(c *startCfg, args []string, io *commands.IO) error { return fmt.Errorf("error in creating node: %w", err) } - fmt.Fprintln(io.Err, "Node created.") + io.ErrPrintln("Node created.") if c.skipStart { - fmt.Fprintln(io.Err, "'--skip-start' is set. Exiting.") - + io.ErrPrintln("'--skip-start' is set. Exiting.") return nil } @@ -170,27 +183,20 @@ func execStart(c *startCfg, args []string, io *commands.IO) error { return fmt.Errorf("error in start node: %w", err) } - // run forever osm.TrapSignal(func() { if gnoNode.IsRunning() { _ = gnoNode.Stop() } }) - select {} // run forever + // Run forever + select {} } -// Makes a local test genesis doc with local privValidator. -func makeGenesisDoc( - pvPub crypto.PubKey, - chainID string, - genesisBalancesFile string, - genesisTxs []std.Tx, -) *bft.GenesisDoc { +func generateGenesisFile(genesisFile string, pk crypto.PubKey, c *startCfg) error { gen := &bft.GenesisDoc{} - gen.GenesisTime = time.Now() - gen.ChainID = chainID + gen.ChainID = c.chainID gen.ConsensusParams = abci.ConsensusParams{ Block: &abci.BlockParams{ // TODO: update limits. @@ -200,121 +206,55 @@ func makeGenesisDoc( TimeIotaMS: 100, // 100ms }, } + gen.Validators = []bft.GenesisValidator{ { - Address: pvPub.Address(), - PubKey: pvPub, + Address: pk.Address(), + PubKey: pk, Power: 10, Name: "testvalidator", }, } - // Load distribution. - balances := loadGenesisBalances(genesisBalancesFile) - // debug: for _, balance := range balances { fmt.Println(balance) } + // Load balances files + balances, err := gnoland.LoadGenesisBalancesFile(c.genesisBalancesFile) + if err != nil { + return fmt.Errorf("unable to load genesis balances file %q: %w", c.genesisBalancesFile, err) + } - // Load initial packages from examples. + // Load examples folder + examplesDir := filepath.Join(c.gnoRootDir, "examples") test1 := crypto.MustAddressFromString("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") - txs := []std.Tx{} - - // List initial packages to load from examples. - pkgs, err := gnomod.ListPkgs(filepath.Join("..", "examples")) - if err != nil { - panic(fmt.Errorf("listing gno packages: %w", err)) + examplePkgs := gnoland.PackagePath{ + Path: examplesDir, + Creator: test1, + Fee: std.NewFee(50000, std.MustParseCoin("1000000ugnot")), + Deposit: nil, } - // Sort packages by dependencies. - sortedPkgs, err := pkgs.Sort() + pkgsTxs, err := examplePkgs.Load() if err != nil { - panic(fmt.Errorf("sorting packages: %w", err)) + return fmt.Errorf("unable to load examples folder: %w", err) } - // Filter out draft packages. - nonDraftPkgs := sortedPkgs.GetNonDraftPkgs() - - for _, pkg := range nonDraftPkgs { - // open files in directory as MemPackage. - memPkg := gno.ReadMemPackage(pkg.Dir, pkg.Name) - - var tx std.Tx - tx.Msgs = []std.Msg{ - vmm.MsgAddPackage{ - Creator: test1, - Package: memPkg, - Deposit: nil, - }, - } - tx.Fee = std.NewFee(50000, std.MustParseCoin("1000000ugnot")) - tx.Signatures = make([]std.Signature, len(tx.GetSigners())) - txs = append(txs, tx) + // Load Genesis TXs + genesisTxs, err := gnoland.LoadGenesisTxsFile(c.genesisTxsFile, c.chainID, c.genesisRemote) + if err != nil { + return fmt.Errorf("unable to load genesis txs file: %w", err) } - // load genesis txs from file. - txs = append(txs, genesisTxs...) + genesisTxs = append(pkgsTxs, genesisTxs...) - // construct genesis AppState. + // Construct genesis AppState. gen.AppState = gnoland.GnoGenesisState{ Balances: balances, - Txs: txs, + Txs: genesisTxs, } - return gen -} -func writeGenesisFile(gen *bft.GenesisDoc, filePath string) { - err := gen.SaveAs(filePath) - if err != nil { - panic(err) + // Write genesis state + if err := gen.SaveAs(genesisFile); err != nil { + return fmt.Errorf("unable to write genesis file %q: %w", genesisFile, err) } -} - -func loadGenesisTxs( - path string, - chainID string, - genesisRemote string, -) []std.Tx { - txs := []std.Tx{} - txsBz := osm.MustReadFile(path) - txsLines := strings.Split(string(txsBz), "\n") - for _, txLine := range txsLines { - if txLine == "" { - continue // skip empty line - } - - // patch the TX - txLine = strings.ReplaceAll(txLine, "%%CHAINID%%", chainID) - txLine = strings.ReplaceAll(txLine, "%%REMOTE%%", genesisRemote) - var tx std.Tx - amino.MustUnmarshalJSON([]byte(txLine), &tx) - txs = append(txs, tx) - } - - return txs -} - -func loadGenesisBalances(path string) []string { - // each balance is in the form: g1xxxxxxxxxxxxxxxx=100000ugnot - balances := []string{} - content := osm.MustReadFile(path) - lines := strings.Split(string(content), "\n") - for _, line := range lines { - line = strings.TrimSpace(line) - - // remove comments. - line = strings.Split(line, "#")[0] - line = strings.TrimSpace(line) - - // skip empty lines. - if line == "" { - continue - } - - parts := strings.Split(line, "=") - if len(parts) != 2 { - panic("invalid genesis_balance line: " + line) - } - - balances = append(balances, line) - } - return balances + return nil } diff --git a/gno.land/pkg/gnoland/types.go b/gno.land/pkg/gnoland/types.go index 7dba82bc6a3..3101e5d7d40 100644 --- a/gno.land/pkg/gnoland/types.go +++ b/gno.land/pkg/gnoland/types.go @@ -1,9 +1,13 @@ package gnoland import ( + "errors" "fmt" "strings" + vmm "github.com/gnolang/gno/gno.land/pkg/sdk/vm" + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/gnovm/pkg/gnomod" bft "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/std" @@ -59,3 +63,57 @@ func (b *Balance) Marshaljson() ([]byte, error) { func (b Balance) String() string { return fmt.Sprintf("%s=%s", b.Address.String(), b.Value.String()) } + +type PackagePath struct { + Creator bft.Address + Deposit std.Coins + Fee std.Fee + Path string +} + +func (p PackagePath) Load() ([]std.Tx, error) { + if p.Creator.IsZero() { + return nil, errors.New("empty creator address") + } + + if p.Path == "" { + return nil, errors.New("empty package path") + } + + // list all packages from target path + pkgs, err := gnomod.ListPkgs(p.Path) + if err != nil { + return nil, fmt.Errorf("listing gno packages: %w", err) + } + + // Sort packages by dependencies. + sortedPkgs, err := pkgs.Sort() + if err != nil { + return nil, fmt.Errorf("sorting packages: %w", err) + } + + // Filter out draft packages. + nonDraftPkgs := sortedPkgs.GetNonDraftPkgs() + txs := []std.Tx{} + for _, pkg := range nonDraftPkgs { + // Open files in directory as MemPackage. + memPkg := gno.ReadMemPackage(pkg.Dir, pkg.Name) + + // Create transaction + tx := std.Tx{ + Fee: p.Fee, + Msgs: []std.Msg{ + vmm.MsgAddPackage{ + Creator: p.Creator, + Package: memPkg, + Deposit: p.Deposit, + }, + }, + } + + tx.Signatures = make([]std.Signature, len(tx.GetSigners())) + txs = append(txs, tx) + } + + return txs, nil +} diff --git a/gno.land/pkg/integration/node.go b/gno.land/pkg/integration/node.go index 10d44db2223..546c2c0b90b 100644 --- a/gno.land/pkg/integration/node.go +++ b/gno.land/pkg/integration/node.go @@ -1,16 +1,12 @@ package integration import ( - "errors" "fmt" "time" "github.com/gnolang/gno/gno.land/pkg/gnoland" - vmm "github.com/gnolang/gno/gno.land/pkg/sdk/vm" - gno "github.com/gnolang/gno/gnovm/pkg/gnolang" - "github.com/gnolang/gno/gnovm/pkg/gnomod" abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" - "github.com/gnolang/gno/tm2/pkg/bft/config" + bftconfig "github.com/gnolang/gno/tm2/pkg/bft/config" "github.com/gnolang/gno/tm2/pkg/bft/node" "github.com/gnolang/gno/tm2/pkg/bft/proxy" bft "github.com/gnolang/gno/tm2/pkg/bft/types" @@ -21,66 +17,57 @@ import ( "github.com/gnolang/gno/tm2/pkg/std" ) -type NodeConfig struct { - BFTConfig *config.Config +type TestingNodeConfig struct { + BFTConfig *bftconfig.Config ConsensusParams abci.ConsensusParams GenesisValidator []bft.GenesisValidator - Packages []PackagePath + Packages []gnoland.PackagePath Balances []gnoland.Balance GenesisTXs []std.Tx SkipFailingGenesisTxs bool GenesisMaxVMCycles int64 } -func NewNode(logger log.Logger, cfg NodeConfig) (*node.Node, error) { - // Setup setup testing config if needed - { - const defaultListner = "tcp://127.0.0.1:0" - if cfg.BFTConfig == nil { - cfg.BFTConfig = config.TestConfig() - cfg.BFTConfig.RPC.ListenAddress = defaultListner - cfg.BFTConfig.P2P.ListenAddress = defaultListner - } +func NewTestingNodeConfig() *TestingNodeConfig { + return &TestingNodeConfig{ + BFTConfig: bftconfig.TestConfig(), + ConsensusParams: abci.ConsensusParams{ + Block: &abci.BlockParams{ + MaxTxBytes: 1_000_000, // 1MB, + MaxDataBytes: 2_000_000, // 2MB, + MaxGas: 10_0000_000, // 10M gas + TimeIotaMS: 100, // 100ms + }, + }, + GenesisMaxVMCycles: 10_000_000, + } +} - // XXX: we need to get ride of this, for now needed because of stdlib - if cfg.BFTConfig.RootDir == "" { - gnoRootDir := gnoland.MustGuessGnoRootDir() - cfg.BFTConfig.SetRootDir(gnoRootDir) - } +func NewTestingNode(logger log.Logger, cfg *TestingNodeConfig) (*node.Node, error) { + if cfg.BFTConfig == nil { + return nil, fmt.Errorf("no BFTConfig given") } - // generate node identity nodekey := &p2p.NodeKey{PrivKey: ed25519.GenPrivKey()} - priv := bft.NewMockPVWithParams(nodekey.PrivKey, false, false) + pv := bft.NewMockPVWithParams(ed25519.GenPrivKey(), false, false) // Setup geeneis gen := &bft.GenesisDoc{} { gen.GenesisTime = time.Now() - // cfg.chainID = "tendermint_test" gen.ChainID = cfg.BFTConfig.ChainID() - // XXX(gfanton): Do we need some default here ? - // if icfg.ConsensusParams.Block == nil { - // icfg.ConsensusParams.Block = &abci.BlockParams{ - // MaxTxBytes: 1000000, // 1MB, - // MaxDataBytes: 2000000, // 2MB, - // MaxGas: 10000000, // 10M gas - // TimeIotaMS: 100, // 100ms - // } - // } gen.ConsensusParams = cfg.ConsensusParams - pk := priv.GetPubKey() - - // start with self validator + // Register self first + pk := pv.GetPubKey() gen.Validators = []bft.GenesisValidator{ { Address: pk.Address(), PubKey: pk, Power: 10, - Name: "rootValidator", + Name: "self", }, } @@ -129,7 +116,7 @@ func NewNode(logger log.Logger, cfg NodeConfig) (*node.Node, error) { } return node.NewNode(cfg.BFTConfig, - priv, nodekey, + pv, nodekey, appClientCreator, genProvider, node.DefaultDBProvider, @@ -137,7 +124,7 @@ func NewNode(logger log.Logger, cfg NodeConfig) (*node.Node, error) { ) } -func LoadPackages(pkgs []PackagePath) ([]std.Tx, error) { +func LoadPackages(pkgs []gnoland.PackagePath) ([]std.Tx, error) { txs := []std.Tx{} for _, pkg := range pkgs { tx, err := pkg.Load() @@ -148,57 +135,3 @@ func LoadPackages(pkgs []PackagePath) ([]std.Tx, error) { } return txs, nil } - -type PackagePath struct { - Creator bft.Address - Deposit std.Coins - Fee std.Fee - Path string -} - -func (p PackagePath) Load() ([]std.Tx, error) { - if p.Creator.IsZero() { - return nil, errors.New("empty creator address") - } - - if p.Path == "" { - return nil, errors.New("empty package path") - } - - // list all packages from target path - pkgs, err := gnomod.ListPkgs(p.Path) - if err != nil { - return nil, fmt.Errorf("listing gno packages: %w", err) - } - - // Sort packages by dependencies. - sortedPkgs, err := pkgs.Sort() - if err != nil { - return nil, fmt.Errorf("sorting packages: %w", err) - } - - // Filter out draft packages. - nonDraftPkgs := sortedPkgs.GetNonDraftPkgs() - txs := []std.Tx{} - for _, pkg := range nonDraftPkgs { - // Open files in directory as MemPackage. - memPkg := gno.ReadMemPackage(pkg.Dir, pkg.Name) - - // Create transaction - tx := std.Tx{ - Fee: p.Fee, - Msgs: []std.Msg{ - vmm.MsgAddPackage{ - Creator: p.Creator, - Package: memPkg, - Deposit: p.Deposit, - }, - }, - } - - tx.Signatures = make([]std.Signature, len(tx.GetSigners())) - txs = append(txs, tx) - } - - return txs, nil -} diff --git a/gno.land/pkg/integration/node_test.go b/gno.land/pkg/integration/node_test.go deleted file mode 100644 index b327117c897..00000000000 --- a/gno.land/pkg/integration/node_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package integration - -import ( - "testing" - - "github.com/gnolang/gno/tm2/pkg/log" - "github.com/jaekwon/testify/require" -) - -func TestNewInMemory(t *testing.T) { - logger := log.TestingLogger() - - node, err := NewNode(logger, NodeConfig{}) - require.NoError(t, err) - require.NotNil(t, node) -} diff --git a/gno.land/pkg/integration/testing_integration.go b/gno.land/pkg/integration/testing_integration.go index 1092230cf5e..da6ac5ad826 100644 --- a/gno.land/pkg/integration/testing_integration.go +++ b/gno.land/pkg/integration/testing_integration.go @@ -10,25 +10,16 @@ import ( "strings" "sync" "testing" - "time" "github.com/gnolang/gno/gno.land/pkg/gnoland" "github.com/gnolang/gno/tm2/pkg/bft/node" - "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/commands" "github.com/gnolang/gno/tm2/pkg/crypto/keys" "github.com/gnolang/gno/tm2/pkg/crypto/keys/client" - "github.com/gnolang/gno/tm2/pkg/events" "github.com/gnolang/gno/tm2/pkg/log" "github.com/rogpeppe/go-internal/testscript" ) -// XXX: This should be centralize somewhere. -const ( - test1Addr = "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5" - test1Seed = "source bonus chronic canvas draft south burst lottery vacant surface solve popular case indicate oppose farm nothing bullet exhibit title speed wink action roast" -) - type testNode struct { *node.Node logger log.Logger @@ -64,8 +55,6 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { var muNodes sync.Mutex nodes := map[string]*testNode{} - nodeConfig := DefaultTestingNodeConfig(t, gnoRootDir) - updateScripts, _ := strconv.ParseBool(os.Getenv("UPDATE_SCRIPTS")) persistWorkDir, _ := strconv.ParseBool(os.Getenv("TESTWORK")) return testscript.Params{ @@ -79,9 +68,11 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { } // XXX: Add a command to add custom account. - kb.CreateAccount("test1", test1Seed, "", "", 0, 0) - env.Setenv("USER_SEED_test1", test1Seed) - env.Setenv("USER_ADDR_test1", test1Addr) + + // Setup "test1" default account + kb.CreateAccount(DefaultAccountName, DefaultAccountSeed, "", "", 0, 0) + env.Setenv("USER_SEED_"+DefaultAccountName, DefaultAccountSeed) + env.Setenv("USER_ADDR_"+DefaultAccountName, DefaultAccountAddress) env.Setenv("GNOROOT", gnoRootDir) env.Setenv("GNOHOME", gnoHomeDir) @@ -117,57 +108,32 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { logger = getTestingLogger(ts, logname) } - var node *node.Node - if node, err = execInMemoryGnoland(logger, nodeConfig); err == nil { - nodes[sid] = &testNode{ - Node: node, - logger: logger, - } - ts.Defer(func() { - muNodes.Lock() - defer muNodes.Unlock() - - if n := nodes[sid]; n != nil { - if err := n.Stop(); err != nil { - panic(fmt.Errorf("node %q was unable to stop: %w", sid, err)) - } - } - }) - - // Get listen address environment. - // It should have been updated with the right port on start. - laddr := node.Config().RPC.ListenAddress - - // Add default environements. - ts.Setenv("RPC_ADDR", laddr) - // ts.Setenv("GNODATA", gnoDataDir) - - // XXX: This should be replace by https://github.com/gnolang/gno/pull/1216 - const listenerID = "testing_listener" - - // Wait for first block by waiting for `EventNewBlock` event. - nb := make(chan struct{}, 1) - node.EventSwitch().AddListener(listenerID, func(ev events.Event) { - if _, ok := ev.(types.EventNewBlock); ok { - select { - case nb <- struct{}{}: - default: - } - } - }) + // Generate node config, we can't use a uniq config, because + nodeConfig := DefaultTestingNodeConfig(t, gnoRootDir) + node, remoteAddr := TestingInMemoryNode(t, logger, nodeConfig) + + // Setup cleanup + ts.Defer(func() { + muNodes.Lock() + defer muNodes.Unlock() - if node.BlockStore().Height() == 0 { - select { - case <-nb: // ok - case <-time.After(time.Second * 6): - ts.Fatalf("timeout while waiting for the node to start") + if n := nodes[sid]; n != nil { + if err := node.Stop(); err != nil { + panic(fmt.Errorf("node %q was unable to stop: %w", sid, err)) } } + }) - node.EventSwitch().RemoveListener(listenerID) - - fmt.Fprintln(ts.Stdout(), "node started successfully") + // Register cleanup. + nodes[sid] = &testNode{ + Node: node, + logger: logger, } + + // Add default environements. + ts.Setenv("RPC_ADDR", remoteAddr) + + fmt.Fprintln(ts.Stdout(), "node started successfully") case "stop": n, ok := nodes[sid] if !ok { @@ -180,7 +146,6 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { // Unset gnoland environements. ts.Setenv("RPC_ADDR", "") - // ts.Setenv("GNODATA", "") fmt.Fprintln(ts.Stdout(), "node stopped successfully") } default: @@ -283,15 +248,6 @@ func getTestingLogger(ts *testscript.TestScript, logname string) log.Logger { return logger } -func execInMemoryGnoland(logger log.Logger, config *NodeConfig) (*node.Node, error) { - node, err := NewNode(logger, *config) - if err != nil { - return nil, fmt.Errorf("unable to start the node: %w", err) - } - - return node, node.Start() -} - func tsValidateError(ts *testscript.TestScript, cmd string, neg bool, err error) { if err != nil { fmt.Fprintf(ts.Stderr(), "%q error: %v\n", cmd, err) diff --git a/gno.land/pkg/integration/testing_node.go b/gno.land/pkg/integration/testing_node.go index ea3ab667f91..c522e1c885c 100644 --- a/gno.land/pkg/integration/testing_node.go +++ b/gno.land/pkg/integration/testing_node.go @@ -17,18 +17,27 @@ import ( "github.com/jaekwon/testify/require" ) +const ( + DefaultAccountName = "test1" + DefaultAccountAddress = "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5" + DefaultAccountSeed = "source bonus chronic canvas draft south burst lottery vacant surface solve popular case indicate oppose farm nothing bullet exhibit title speed wink action roast" +) + // Should return an already starting node -func TestingInMemoryNode(t *testing.T, logger log.Logger, config *NodeConfig) (*node.Node, string) { - node, err := NewNode(logger, *config) +func TestingInMemoryNode(t *testing.T, logger log.Logger, config *TestingNodeConfig) (*node.Node, string) { + t.Helper() + + node, err := NewTestingNode(logger, config) require.NoError(t, err) err = node.Start() require.NoError(t, err) // XXX: This should be replace by https://github.com/gnolang/gno/pull/1216 + //--- + // Wait for first block by waiting for `EventNewBlock` event. const listenerID = "testing_listener" - // Wait for first block by waiting for `EventNewBlock` event. nb := make(chan struct{}, 1) node.EventSwitch().AddListener(listenerID, func(ev events.Event) { if _, ok := ev.(bft.EventNewBlock); ok { @@ -48,36 +57,36 @@ func TestingInMemoryNode(t *testing.T, logger log.Logger, config *NodeConfig) (* } node.EventSwitch().RemoveListener(listenerID) + // --- return node, node.Config().RPC.ListenAddress } -func DefaultTestingNodeConfig(t *testing.T, gnoroot string) *NodeConfig { +func DefaultTestingNodeConfig(t *testing.T, gnoroot string) *TestingNodeConfig { t.Helper() bftconfig := DefaultTestingBFTConfig(t, gnoroot) - - return &NodeConfig{ - Balances: LoadDefaultGenesisBalanceFile(t, gnoroot), - GenesisTXs: LoadDefaultGenesisTXsFile(t, bftconfig.ChainID(), gnoroot), - BFTConfig: bftconfig, - Packages: LoadDefaultPackages(t, crypto.MustAddressFromString(test1Addr), gnoroot), + return &TestingNodeConfig{ + Balances: LoadDefaultGenesisBalanceFile(t, gnoroot), + GenesisTXs: LoadDefaultGenesisTXsFile(t, bftconfig.ChainID(), gnoroot), + ConsensusParams: DefaultConsensusParams(t), + BFTConfig: bftconfig, + Packages: LoadDefaultPackages(t, crypto.MustAddressFromString(DefaultAccountAddress), gnoroot), } } -func LoadDefaultPackages(t *testing.T, creator bft.Address, gnoroot string) []PackagePath { +func LoadDefaultPackages(t *testing.T, creator bft.Address, gnoroot string) []gnoland.PackagePath { t.Helper() exampleDir := filepath.Join(gnoroot, "examples") - pkgs := PackagePath{ - // Creator: crypto.MustAddressFromString(test1Addr), + pkgs := gnoland.PackagePath{ Creator: creator, Fee: std.NewFee(50000, std.MustParseCoin("1000000ugnot")), Path: exampleDir, } - return []PackagePath{pkgs} + return []gnoland.PackagePath{pkgs} } func LoadDefaultGenesisBalanceFile(t *testing.T, gnoroot string) []gnoland.Balance { @@ -104,15 +113,15 @@ func LoadDefaultGenesisTXsFile(t *testing.T, chainid string, gnoroot string) []s return genesisTXs } -func DefaultConsensusParams(t *testing.T, gnoroot string) *abci.ConsensusParams { +func DefaultConsensusParams(t *testing.T) abci.ConsensusParams { t.Helper() - return &abci.ConsensusParams{ + return abci.ConsensusParams{ Block: &abci.BlockParams{ - MaxTxBytes: 1000000, // 1MB, - MaxDataBytes: 2000000, // 2MB, - MaxGas: 10000000, // 10M gas - TimeIotaMS: 100, // 100ms + MaxTxBytes: 1_000_000, // 1MB, + MaxDataBytes: 2_000_000, // 2MB, + MaxGas: 10_000_000, // 10M gas + TimeIotaMS: 100, // 100ms }, } } From 19cfdb08ce41455254e680774ed8b241373f7232 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Mon, 16 Oct 2023 15:10:11 +0200 Subject: [PATCH 15/38] chore: lint Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/cmd/gnoweb/main_test.go | 3 +- .../node.go => gnoland/node_inmemory.go} | 58 ++++++------ .../pkg/integration/testing_integration.go | 27 ++++-- gno.land/pkg/integration/testing_node.go | 88 ++++++++++--------- 4 files changed, 99 insertions(+), 77 deletions(-) rename gno.land/pkg/{integration/node.go => gnoland/node_inmemory.go} (64%) diff --git a/gno.land/cmd/gnoweb/main_test.go b/gno.land/cmd/gnoweb/main_test.go index d395c442fbf..e95c04dd1f6 100644 --- a/gno.land/cmd/gnoweb/main_test.go +++ b/gno.land/cmd/gnoweb/main_test.go @@ -48,7 +48,8 @@ func TestRoutes(t *testing.T) { node, remoteAddr := integration.TestingInMemoryNode(t, log.NewNopLogger(), config) defer node.Stop() - // set the `remoteAddr` of the client to the listening address of the node, which is randomly assigned. + // set the `remoteAddr` of the client to the listening address of the + // node, which is randomly assigned. flags.remoteAddr = remoteAddr app := makeApp() diff --git a/gno.land/pkg/integration/node.go b/gno.land/pkg/gnoland/node_inmemory.go similarity index 64% rename from gno.land/pkg/integration/node.go rename to gno.land/pkg/gnoland/node_inmemory.go index 546c2c0b90b..fc422903bec 100644 --- a/gno.land/pkg/integration/node.go +++ b/gno.land/pkg/gnoland/node_inmemory.go @@ -1,12 +1,11 @@ -package integration +package gnoland import ( "fmt" "time" - "github.com/gnolang/gno/gno.land/pkg/gnoland" abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" - bftconfig "github.com/gnolang/gno/tm2/pkg/bft/config" + tmcfg "github.com/gnolang/gno/tm2/pkg/bft/config" "github.com/gnolang/gno/tm2/pkg/bft/node" "github.com/gnolang/gno/tm2/pkg/bft/proxy" bft "github.com/gnolang/gno/tm2/pkg/bft/types" @@ -17,20 +16,20 @@ import ( "github.com/gnolang/gno/tm2/pkg/std" ) -type TestingNodeConfig struct { - BFTConfig *bftconfig.Config +type InMemoryNodeConfig struct { + TMConfig *tmcfg.Config ConsensusParams abci.ConsensusParams GenesisValidator []bft.GenesisValidator - Packages []gnoland.PackagePath - Balances []gnoland.Balance + Packages []PackagePath + Balances []Balance GenesisTXs []std.Tx SkipFailingGenesisTxs bool GenesisMaxVMCycles int64 } -func NewTestingNodeConfig() *TestingNodeConfig { - return &TestingNodeConfig{ - BFTConfig: bftconfig.TestConfig(), +func NewInMemoryNodeConfig(tmcfg *tmcfg.Config) *InMemoryNodeConfig { + return &InMemoryNodeConfig{ + TMConfig: tmcfg, ConsensusParams: abci.ConsensusParams{ Block: &abci.BlockParams{ MaxTxBytes: 1_000_000, // 1MB, @@ -43,11 +42,16 @@ func NewTestingNodeConfig() *TestingNodeConfig { } } -func NewTestingNode(logger log.Logger, cfg *TestingNodeConfig) (*node.Node, error) { - if cfg.BFTConfig == nil { - return nil, fmt.Errorf("no BFTConfig given") +// NewInMemoryNode create an inMemeory gnoland node. In this mode, the node will +// not persist any data using an InMemory Database. For now the only indirect +// requirement is that `InMemoryNodeConfig.TMConfig.RootDir` is pointing to +// correct gno repository so that the node can load stdlibs +func NewInMemoryNode(logger log.Logger, cfg *InMemoryNodeConfig) (*node.Node, error) { + if cfg.TMConfig == nil { + return nil, fmt.Errorf("no TMConfig given") } + // Create Identity nodekey := &p2p.NodeKey{PrivKey: ed25519.GenPrivKey()} pv := bft.NewMockPVWithParams(ed25519.GenPrivKey(), false, false) @@ -56,7 +60,7 @@ func NewTestingNode(logger log.Logger, cfg *TestingNodeConfig) (*node.Node, erro { gen.GenesisTime = time.Now() - gen.ChainID = cfg.BFTConfig.ChainID() + gen.ChainID = cfg.TMConfig.ChainID() gen.ConsensusParams = cfg.ConsensusParams @@ -76,7 +80,7 @@ func NewTestingNode(logger log.Logger, cfg *TestingNodeConfig) (*node.Node, erro } } - // XXX: maybe let the user do this manually and pass it to genesisTXs + // XXX: Maybe let the user do this manually and pass it to genesisTXs txs, err := LoadPackages(cfg.Packages) if err != nil { return nil, fmt.Errorf("uanble to load genesis packages: %w", err) @@ -84,14 +88,14 @@ func NewTestingNode(logger log.Logger, cfg *TestingNodeConfig) (*node.Node, erro txs = append(txs, cfg.GenesisTXs...) - gen.AppState = gnoland.GnoGenesisState{ + gen.AppState = GnoGenesisState{ Balances: cfg.Balances, Txs: txs, } - gnoApp, err := gnoland.NewAppWithOptions(&gnoland.AppOptions{ + gnoApp, err := NewAppWithOptions(&AppOptions{ Logger: logger, - GnoRootDir: cfg.BFTConfig.RootDir, + GnoRootDir: cfg.TMConfig.RootDir, SkipFailingGenesisTxs: cfg.SkipFailingGenesisTxs, MaxCycles: cfg.GenesisMaxVMCycles, DB: db.NewMemDB(), @@ -100,22 +104,22 @@ func NewTestingNode(logger log.Logger, cfg *TestingNodeConfig) (*node.Node, erro return nil, fmt.Errorf("error in creating new app: %w", err) } - cfg.BFTConfig.LocalApp = gnoApp + cfg.TMConfig.LocalApp = gnoApp - // Get app client creator. + // Get app client creator appClientCreator := proxy.DefaultClientCreator( - cfg.BFTConfig.LocalApp, - cfg.BFTConfig.ProxyApp, - cfg.BFTConfig.ABCI, - cfg.BFTConfig.DBDir(), + cfg.TMConfig.LocalApp, + cfg.TMConfig.ProxyApp, + cfg.TMConfig.ABCI, + cfg.TMConfig.DBDir(), ) - // Create genesis factory. + // Create genesis factory genProvider := func() (*bft.GenesisDoc, error) { return gen, nil } - return node.NewNode(cfg.BFTConfig, + return node.NewNode(cfg.TMConfig, pv, nodekey, appClientCreator, genProvider, @@ -124,7 +128,7 @@ func NewTestingNode(logger log.Logger, cfg *TestingNodeConfig) (*node.Node, erro ) } -func LoadPackages(pkgs []gnoland.PackagePath) ([]std.Tx, error) { +func LoadPackages(pkgs []PackagePath) ([]std.Tx, error) { txs := []std.Tx{} for _, pkg := range pkgs { tx, err := pkg.Load() diff --git a/gno.land/pkg/integration/testing_integration.go b/gno.land/pkg/integration/testing_integration.go index da6ac5ad826..be87ea1135a 100644 --- a/gno.land/pkg/integration/testing_integration.go +++ b/gno.land/pkg/integration/testing_integration.go @@ -12,6 +12,7 @@ import ( "testing" "github.com/gnolang/gno/gno.land/pkg/gnoland" + "github.com/gnolang/gno/tm2/pkg/bft/config" "github.com/gnolang/gno/tm2/pkg/bft/node" "github.com/gnolang/gno/tm2/pkg/commands" "github.com/gnolang/gno/tm2/pkg/crypto/keys" @@ -47,10 +48,6 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { // `gnoHomeDir` should be the local directory where gnokey stores keys. gnoHomeDir := filepath.Join(tmpdir, "gno") - // `gnoDataDir` should refer to the local location where the gnoland node - // stores its configuration and data. - // gnoDataDir := filepath.Join(tmpdir, "data") - // Testscripts run concurrently by default, so we need to be prepared for that. var muNodes sync.Mutex nodes := map[string]*testNode{} @@ -108,9 +105,21 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { logger = getTestingLogger(ts, logname) } - // Generate node config, we can't use a uniq config, because - nodeConfig := DefaultTestingNodeConfig(t, gnoRootDir) - node, remoteAddr := TestingInMemoryNode(t, logger, nodeConfig) + // Generate node config + cfg := config.TestConfig().SetRootDir(gnoRootDir) + + // Use random port be able to run test in parallel without conflict + cfg.P2P.ListenAddress = "tcp://127.0.0.1:0" + cfg.RPC.ListenAddress = "tcp://127.0.0.1:0" + + // Create the Node + nodecfg := gnoland.NewInMemoryNodeConfig(cfg) + + node, err := gnoland.NewInMemoryNode(logger, nodecfg) + if err != nil { + err = fmt.Errorf("unable to start node: %w", err) + break + } // Setup cleanup ts.Defer(func() { @@ -130,6 +139,8 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { logger: logger, } + remoteAddr := node.Config().RPC.ListenAddress + // Add default environements. ts.Setenv("RPC_ADDR", remoteAddr) @@ -256,7 +267,7 @@ func tsValidateError(ts *testscript.TestScript, cmd string, neg bool, err error) } } else { if neg { - ts.Fatalf("unexpected %s command success", cmd) + ts.Fatalf("unexpected %q command success", cmd) } } } diff --git a/gno.land/pkg/integration/testing_node.go b/gno.land/pkg/integration/testing_node.go index c522e1c885c..dcb9083e994 100644 --- a/gno.land/pkg/integration/testing_node.go +++ b/gno.land/pkg/integration/testing_node.go @@ -2,12 +2,13 @@ package integration import ( "path/filepath" + "sync" "testing" "time" "github.com/gnolang/gno/gno.land/pkg/gnoland" abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" - "github.com/gnolang/gno/tm2/pkg/bft/config" + tmcfg "github.com/gnolang/gno/tm2/pkg/bft/config" "github.com/gnolang/gno/tm2/pkg/bft/node" bft "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/crypto" @@ -24,53 +25,33 @@ const ( ) // Should return an already starting node -func TestingInMemoryNode(t *testing.T, logger log.Logger, config *TestingNodeConfig) (*node.Node, string) { +func TestingInMemoryNode(t *testing.T, logger log.Logger, config *gnoland.InMemoryNodeConfig) (*node.Node, string) { t.Helper() - node, err := NewTestingNode(logger, config) + node, err := gnoland.NewInMemoryNode(logger, config) require.NoError(t, err) err = node.Start() require.NoError(t, err) - // XXX: This should be replace by https://github.com/gnolang/gno/pull/1216 - //--- - // Wait for first block by waiting for `EventNewBlock` event. - const listenerID = "testing_listener" - - nb := make(chan struct{}, 1) - node.EventSwitch().AddListener(listenerID, func(ev events.Event) { - if _, ok := ev.(bft.EventNewBlock); ok { - select { - case nb <- struct{}{}: - default: - } - } - }) - - if node.BlockStore().Height() == 0 { - select { - case <-nb: // ok - case <-time.After(time.Second * 6): - t.Fatal("timeout while waiting for the node to start") - } + select { + case <-waitForNodeReadiness(node): + case <-time.After(time.Second * 6): + require.FailNow(t, "timeout while waiting for the node to start") } - node.EventSwitch().RemoveListener(listenerID) - // --- - return node, node.Config().RPC.ListenAddress } -func DefaultTestingNodeConfig(t *testing.T, gnoroot string) *TestingNodeConfig { +func DefaultTestingNodeConfig(t *testing.T, gnoroot string) *gnoland.InMemoryNodeConfig { t.Helper() - bftconfig := DefaultTestingBFTConfig(t, gnoroot) - return &TestingNodeConfig{ + tmconfig := DefaultTestingTMConfig(t, gnoroot) + return &gnoland.InMemoryNodeConfig{ Balances: LoadDefaultGenesisBalanceFile(t, gnoroot), - GenesisTXs: LoadDefaultGenesisTXsFile(t, bftconfig.ChainID(), gnoroot), + GenesisTXs: LoadDefaultGenesisTXsFile(t, tmconfig.ChainID(), gnoroot), ConsensusParams: DefaultConsensusParams(t), - BFTConfig: bftconfig, + TMConfig: tmconfig, Packages: LoadDefaultPackages(t, crypto.MustAddressFromString(DefaultAccountAddress), gnoroot), } } @@ -105,8 +86,8 @@ func LoadDefaultGenesisTXsFile(t *testing.T, chainid string, gnoroot string) []s txsFile := filepath.Join(gnoroot, "gno.land", "genesis", "genesis_txs.txt") - // NOTE: we dont care about giving a correct address here, as it's only visual - // XXX: do we care loading this file ? + // NOTE: We dont care about giving a correct address here, as it's only for display + // XXX: Do we care loading this TXs for testing ? genesisTXs, err := gnoland.LoadGenesisTxsFile(txsFile, chainid, "https://127.0.0.1:26657") require.NoError(t, err) @@ -126,15 +107,40 @@ func DefaultConsensusParams(t *testing.T) abci.ConsensusParams { } } -func DefaultTestingBFTConfig(t *testing.T, gnoroot string) *config.Config { +func DefaultTestingTMConfig(t *testing.T, gnoroot string) *tmcfg.Config { t.Helper() const defaultListner = "tcp://127.0.0.1:0" - bftconfig := config.TestConfig().SetRootDir(gnoroot) - bftconfig.Consensus.CreateEmptyBlocks = true - bftconfig.Consensus.CreateEmptyBlocksInterval = time.Duration(0) - bftconfig.RPC.ListenAddress = defaultListner - bftconfig.P2P.ListenAddress = defaultListner - return bftconfig + tmconfig := tmcfg.TestConfig().SetRootDir(gnoroot) + tmconfig.Consensus.CreateEmptyBlocks = true + tmconfig.Consensus.CreateEmptyBlocksInterval = time.Duration(0) + tmconfig.RPC.ListenAddress = defaultListner + tmconfig.P2P.ListenAddress = defaultListner + return tmconfig +} + +// XXX: This should be replace by https://github.com/gnolang/gno/pull/1216 +func waitForNodeReadiness(n *node.Node) <-chan struct{} { + const listenerID = "first_block_listener" + + var once sync.Once + + nb := make(chan struct{}) + ready := func() { + close(nb) + n.EventSwitch().RemoveListener(listenerID) + } + + n.EventSwitch().AddListener(listenerID, func(ev events.Event) { + if _, ok := ev.(bft.EventNewBlock); ok { + once.Do(ready) + } + }) + + if n.BlockStore().Height() > 0 { + once.Do(ready) + } + + return nb } From 7300c4e99fe47654d250eadfa3be2e4a825b0d2d Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 19 Oct 2023 09:01:49 -0400 Subject: [PATCH 16/38] fix(integration): waiting for node readiness Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/cmd/gnoland/testdata/addpkg.txtar | 11 +- .../pkg/integration/testing_integration.go | 120 +++++++++++------- 2 files changed, 79 insertions(+), 52 deletions(-) diff --git a/gno.land/cmd/gnoland/testdata/addpkg.txtar b/gno.land/cmd/gnoland/testdata/addpkg.txtar index 5e871b058ac..5f1ee0caf49 100644 --- a/gno.land/cmd/gnoland/testdata/addpkg.txtar +++ b/gno.land/cmd/gnoland/testdata/addpkg.txtar @@ -10,7 +10,11 @@ gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/foobar/bar -gas-fee 10000 gnokey maketx call -pkgpath gno.land/r/foobar/bar -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1 ## compare render -cmp stdout stdout.golden +stdout '("hello from foo" string)' +stdout 'OK!' +stdout 'GAS WANTED: 2000000' +stdout 'GAS USED: [0-9]+' + -- bar.gno -- package bar @@ -19,8 +23,3 @@ func Render(path string) string { return "hello from foo" } --- stdout.golden -- -("hello from foo" string) -OK! -GAS WANTED: 2000000 -GAS USED: 69163 \ No newline at end of file diff --git a/gno.land/pkg/integration/testing_integration.go b/gno.land/pkg/integration/testing_integration.go index be87ea1135a..6c11838a45c 100644 --- a/gno.land/pkg/integration/testing_integration.go +++ b/gno.land/pkg/integration/testing_integration.go @@ -2,6 +2,7 @@ package integration import ( "context" + "errors" "fmt" "hash/crc32" "os" @@ -10,20 +11,22 @@ import ( "strings" "sync" "testing" + "time" "github.com/gnolang/gno/gno.land/pkg/gnoland" "github.com/gnolang/gno/tm2/pkg/bft/config" "github.com/gnolang/gno/tm2/pkg/bft/node" "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/crypto/keys" "github.com/gnolang/gno/tm2/pkg/crypto/keys/client" "github.com/gnolang/gno/tm2/pkg/log" + "github.com/gnolang/gno/tm2/pkg/std" "github.com/rogpeppe/go-internal/testscript" ) type testNode struct { *node.Node - logger log.Logger nGnoKeyExec uint // Counter for execution of gnokey. } @@ -64,10 +67,33 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { return err } - // XXX: Add a command to add custom account. + // create sessions ID + var sid string + { + works := env.Getenv("WORK") + sum := crc32.ChecksumIEEE([]byte(works)) + sid = strconv.FormatUint(uint64(sum), 16) + env.Setenv("SID", sid) + } + + // setup logger + var logger log.Logger + { + logger = log.NewNopLogger() + if persistWorkDir || os.Getenv("LOG_DIR") != "" { + logname := fmt.Sprintf("gnoland-%s.log", sid) + logger, err = getTestingLogger(env, logname) + if err != nil { + return fmt.Errorf("unable to setup logger: %w", err) + } + } + + env.Values["_logger"] = logger + } // Setup "test1" default account kb.CreateAccount(DefaultAccountName, DefaultAccountSeed, "", "", 0, 0) + env.Setenv("USER_SEED_"+DefaultAccountName, DefaultAccountSeed) env.Setenv("USER_ADDR_"+DefaultAccountName, DefaultAccountAddress) @@ -86,7 +112,8 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { return } - sid := getSessionID(ts) + logger := ts.Value("_logger").(log.Logger) // grab logger + sid := ts.Getenv("SID") // grab session id var cmd string cmd, args = args[0], args[1:] @@ -99,12 +126,6 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { break } - logger := log.NewNopLogger() - if persistWorkDir || os.Getenv("LOG_DIR") != "" { - logname := fmt.Sprintf("gnoland-%s.log", sid) - logger = getTestingLogger(ts, logname) - } - // Generate node config cfg := config.TestConfig().SetRootDir(gnoRootDir) @@ -115,31 +136,21 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { // Create the Node nodecfg := gnoland.NewInMemoryNodeConfig(cfg) - node, err := gnoland.NewInMemoryNode(logger, nodecfg) - if err != nil { - err = fmt.Errorf("unable to start node: %w", err) + // Setup balance for default account + nodecfg.Balances = append(nodecfg.Balances, gnoland.Balance{ + Address: crypto.MustAddressFromString(DefaultAccountAddress), + Value: std.MustParseCoins("10000000000000ugnot"), + }) + + var n *node.Node + if n, err = execInMemoryGnoland(ts, logger, nodecfg); err != nil { break } - // Setup cleanup - ts.Defer(func() { - muNodes.Lock() - defer muNodes.Unlock() - - if n := nodes[sid]; n != nil { - if err := node.Stop(); err != nil { - panic(fmt.Errorf("node %q was unable to stop: %w", sid, err)) - } - } - }) - // Register cleanup. - nodes[sid] = &testNode{ - Node: node, - logger: logger, - } + nodes[sid] = &testNode{Node: n} - remoteAddr := node.Config().RPC.ListenAddress + remoteAddr := n.Config().RPC.ListenAddress // Add default environements. ts.Setenv("RPC_ADDR", remoteAddr) @@ -169,7 +180,8 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { muNodes.Lock() defer muNodes.Unlock() - sid := getSessionID(ts) + logger := ts.Value("_logger").(log.Logger) // grab logger + sid := ts.Getenv("SID") // grab session id // Setup IO command. io := commands.NewTestIO() @@ -190,9 +202,10 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { n.nGnoKeyExec++ headerlog := fmt.Sprintf("%.02d!EXEC_GNOKEY", n.nGnoKeyExec) + // Log the command inside gnoland logger, so we can better scope errors. - n.logger.Info(headerlog, strings.Join(args, " ")) - defer n.logger.Info(headerlog, "END") + logger.Info(headerlog, strings.Join(args, " ")) + defer logger.Info(headerlog, "END") } // Inject default argument, if duplicate @@ -208,35 +221,50 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { } } -func getSessionID(ts *testscript.TestScript) string { - works := ts.Getenv("WORK") - sum := crc32.ChecksumIEEE([]byte(works)) - return strconv.FormatUint(uint64(sum), 16) +func execInMemoryGnoland(ts *testscript.TestScript, logger log.Logger, nodecfg *gnoland.InMemoryNodeConfig) (*node.Node, error) { + n, err := gnoland.NewInMemoryNode(logger, nodecfg) + if err != nil { + return nil, fmt.Errorf("unable to create node: %w", err) + } + + if err = n.Start(); err != nil { + return nil, fmt.Errorf("unable to start node: %w", err) + } + + select { + case <-time.After(time.Second * 6): + return nil, errors.New("timeout while waiting for the node to start") + case <-waitForNodeReadiness(n): // ok + } + + return n, nil } -func getTestingLogger(ts *testscript.TestScript, logname string) log.Logger { +func getTestingLogger(env *testscript.Env, logname string) (log.Logger, error) { var path string + if logdir := os.Getenv("LOG_DIR"); logdir != "" { if err := os.MkdirAll(logdir, 0o755); err != nil { - ts.Fatalf("unable to make log directory %q", logdir) + return nil, fmt.Errorf("unable to make log directory %q", logdir) + } var err error if path, err = filepath.Abs(filepath.Join(logdir, logname)); err != nil { - ts.Fatalf("uanble to get absolute path of logdir %q", logdir) + return nil, fmt.Errorf("uanble to get absolute path of logdir %q", logdir) } - } else if workdir := ts.Getenv("WORK"); workdir != "" { + } else if workdir := env.Getenv("WORK"); workdir != "" { path = filepath.Join(workdir, logname) } else { - return log.NewNopLogger() + return log.NewNopLogger(), nil } f, err := os.Create(path) if err != nil { - ts.Fatalf("unable to create log file %q: %s", path, err.Error()) + return nil, fmt.Errorf("unable to create log file %q: %w", path, err) } - ts.Defer(func() { + env.Defer(func() { if err := f.Close(); err != nil { panic(fmt.Errorf("unable to close log file %q: %w", path, err)) } @@ -252,11 +280,11 @@ func getTestingLogger(ts *testscript.TestScript, logname string) log.Logger { logger.SetLevel(log.LevelInfo) case "": default: - ts.Fatalf("invalid log level %q", level) + return nil, fmt.Errorf("invalid log level %q", level) } - ts.Logf("starting logger: %q", path) - return logger + env.T().Log("starting logger: %q", path) + return logger, nil } func tsValidateError(ts *testscript.TestScript, cmd string, neg bool, err error) { From 7ee015b16078bbacac32f2ef712d6355d366943e Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 19 Oct 2023 10:33:37 -0400 Subject: [PATCH 17/38] chore: lint Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/pkg/integration/testing_integration.go | 1 - 1 file changed, 1 deletion(-) diff --git a/gno.land/pkg/integration/testing_integration.go b/gno.land/pkg/integration/testing_integration.go index 6c11838a45c..004bd9260da 100644 --- a/gno.land/pkg/integration/testing_integration.go +++ b/gno.land/pkg/integration/testing_integration.go @@ -246,7 +246,6 @@ func getTestingLogger(env *testscript.Env, logname string) (log.Logger, error) { if logdir := os.Getenv("LOG_DIR"); logdir != "" { if err := os.MkdirAll(logdir, 0o755); err != nil { return nil, fmt.Errorf("unable to make log directory %q", logdir) - } var err error From aee3dd9cb6337b71d0774a16fc40fbecf279baa9 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Fri, 20 Oct 2023 10:08:29 -0400 Subject: [PATCH 18/38] chore: improve comments Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/pkg/gnoland/genesis.go | 2 +- gno.land/pkg/gnoland/node_inmemory.go | 60 ++++++++++++------------ gno.land/pkg/integration/testing_node.go | 10 +++- 3 files changed, 39 insertions(+), 33 deletions(-) diff --git a/gno.land/pkg/gnoland/genesis.go b/gno.land/pkg/gnoland/genesis.go index b155b212219..9b527590ae7 100644 --- a/gno.land/pkg/gnoland/genesis.go +++ b/gno.land/pkg/gnoland/genesis.go @@ -53,7 +53,7 @@ func LoadGenesisBalancesFile(path string) ([]Balance, error) { return balances, nil } -// XXX: we can do something better here +// XXX: Improve the way we generate and load this file func LoadGenesisTxsFile(path string, chainID string, genesisRemote string) ([]std.Tx, error) { txs := []std.Tx{} txsBz := osm.MustReadFile(path) diff --git a/gno.land/pkg/gnoland/node_inmemory.go b/gno.land/pkg/gnoland/node_inmemory.go index fc422903bec..bb677188fee 100644 --- a/gno.land/pkg/gnoland/node_inmemory.go +++ b/gno.land/pkg/gnoland/node_inmemory.go @@ -27,6 +27,7 @@ type InMemoryNodeConfig struct { GenesisMaxVMCycles int64 } +// NewInMemoryNodeConfig creates a default configuration for an in-memory node. func NewInMemoryNodeConfig(tmcfg *tmcfg.Config) *InMemoryNodeConfig { return &InMemoryNodeConfig{ TMConfig: tmcfg, @@ -42,57 +43,52 @@ func NewInMemoryNodeConfig(tmcfg *tmcfg.Config) *InMemoryNodeConfig { } } -// NewInMemoryNode create an inMemeory gnoland node. In this mode, the node will -// not persist any data using an InMemory Database. For now the only indirect -// requirement is that `InMemoryNodeConfig.TMConfig.RootDir` is pointing to -// correct gno repository so that the node can load stdlibs +// NewInMemoryNode creates an in-memory gnoland node. In this mode, the node does not +// persist any data and uses an in-memory database. The `InMemoryNodeConfig.TMConfig.RootDir` +// should point to the correct gno repository to load the stdlibs. func NewInMemoryNode(logger log.Logger, cfg *InMemoryNodeConfig) (*node.Node, error) { if cfg.TMConfig == nil { - return nil, fmt.Errorf("no TMConfig given") + return nil, fmt.Errorf("no `TMConfig` given") + } + + if cfg.TMConfig.RootDir == "" { + return nil, fmt.Errorf("`TMConfig.RootDir` is required but not provided") } // Create Identity nodekey := &p2p.NodeKey{PrivKey: ed25519.GenPrivKey()} pv := bft.NewMockPVWithParams(ed25519.GenPrivKey(), false, false) - // Setup geeneis - gen := &bft.GenesisDoc{} - { - gen.GenesisTime = time.Now() - - gen.ChainID = cfg.TMConfig.ChainID() - - gen.ConsensusParams = cfg.ConsensusParams - - // Register self first - pk := pv.GetPubKey() - gen.Validators = []bft.GenesisValidator{ + // Set up genesis with default values and additional validators + gen := &bft.GenesisDoc{ + GenesisTime: time.Now(), + ChainID: cfg.TMConfig.ChainID(), + ConsensusParams: cfg.ConsensusParams, + Validators: []bft.GenesisValidator{ { - Address: pk.Address(), - PubKey: pk, + Address: pv.GetPubKey().Address(), + PubKey: pv.GetPubKey(), Power: 10, Name: "self", }, - } - - for _, validator := range cfg.GenesisValidator { - gen.Validators = append(gen.Validators, validator) - } + }, } + gen.Validators = append(gen.Validators, cfg.GenesisValidator...) // XXX: Maybe let the user do this manually and pass it to genesisTXs txs, err := LoadPackages(cfg.Packages) if err != nil { - return nil, fmt.Errorf("uanble to load genesis packages: %w", err) + return nil, fmt.Errorf("error loading genesis packages: %w", err) } + // Combine loaded packages with provided genesis transactions txs = append(txs, cfg.GenesisTXs...) - gen.AppState = GnoGenesisState{ Balances: cfg.Balances, Txs: txs, } + // Initialize the application with the provided options gnoApp, err := NewAppWithOptions(&AppOptions{ Logger: logger, GnoRootDir: cfg.TMConfig.RootDir, @@ -101,12 +97,12 @@ func NewInMemoryNode(logger log.Logger, cfg *InMemoryNodeConfig) (*node.Node, er DB: db.NewMemDB(), }) if err != nil { - return nil, fmt.Errorf("error in creating new app: %w", err) + return nil, fmt.Errorf("error initializing new app: %w", err) } cfg.TMConfig.LocalApp = gnoApp - // Get app client creator + // Setup app client creator appClientCreator := proxy.DefaultClientCreator( cfg.TMConfig.LocalApp, cfg.TMConfig.ProxyApp, @@ -119,21 +115,23 @@ func NewInMemoryNode(logger log.Logger, cfg *InMemoryNodeConfig) (*node.Node, er return gen, nil } + // Create and return the in-memory node instance return node.NewNode(cfg.TMConfig, pv, nodekey, appClientCreator, genProvider, node.DefaultDBProvider, - logger, + logger ) } +// LoadPackages loads and returns transactions from provided package paths. func LoadPackages(pkgs []PackagePath) ([]std.Tx, error) { - txs := []std.Tx{} + var txs []std.Tx for _, pkg := range pkgs { tx, err := pkg.Load() if err != nil { - return nil, fmt.Errorf("unable to load packages: %w", err) + return nil, fmt.Errorf("error loading package from path %s: %w", pkg.Path, err) } txs = append(txs, tx...) } diff --git a/gno.land/pkg/integration/testing_node.go b/gno.land/pkg/integration/testing_node.go index dcb9083e994..80de8ecc811 100644 --- a/gno.land/pkg/integration/testing_node.go +++ b/gno.land/pkg/integration/testing_node.go @@ -24,7 +24,8 @@ const ( DefaultAccountSeed = "source bonus chronic canvas draft south burst lottery vacant surface solve popular case indicate oppose farm nothing bullet exhibit title speed wink action roast" ) -// Should return an already starting node +// TestingInMemoryNode initializes and starts an in-memory node for testing. +// It returns the node instance and its RPC remote address. func TestingInMemoryNode(t *testing.T, logger log.Logger, config *gnoland.InMemoryNodeConfig) (*node.Node, string) { t.Helper() @@ -43,6 +44,7 @@ func TestingInMemoryNode(t *testing.T, logger log.Logger, config *gnoland.InMemo return node, node.Config().RPC.ListenAddress } +// DefaultTestingNodeConfig constructs the default in-memory node configuration for testing. func DefaultTestingNodeConfig(t *testing.T, gnoroot string) *gnoland.InMemoryNodeConfig { t.Helper() @@ -56,6 +58,7 @@ func DefaultTestingNodeConfig(t *testing.T, gnoroot string) *gnoland.InMemoryNod } } +// LoadDefaultPackages loads the default packages for testing using a given creator address and gnoroot directory. func LoadDefaultPackages(t *testing.T, creator bft.Address, gnoroot string) []gnoland.PackagePath { t.Helper() @@ -70,6 +73,7 @@ func LoadDefaultPackages(t *testing.T, creator bft.Address, gnoroot string) []gn return []gnoland.PackagePath{pkgs} } +// LoadDefaultGenesisBalanceFile loads the default genesis balance file for testing. func LoadDefaultGenesisBalanceFile(t *testing.T, gnoroot string) []gnoland.Balance { t.Helper() @@ -81,6 +85,7 @@ func LoadDefaultGenesisBalanceFile(t *testing.T, gnoroot string) []gnoland.Balan return genesisBalances } +// LoadDefaultGenesisTXsFile loads the default genesis transactions file for testing. func LoadDefaultGenesisTXsFile(t *testing.T, chainid string, gnoroot string) []std.Tx { t.Helper() @@ -94,6 +99,7 @@ func LoadDefaultGenesisTXsFile(t *testing.T, chainid string, gnoroot string) []s return genesisTXs } +// DefaultConsensusParams constructs the default consensus parameters for testing. func DefaultConsensusParams(t *testing.T) abci.ConsensusParams { t.Helper() @@ -107,6 +113,7 @@ func DefaultConsensusParams(t *testing.T) abci.ConsensusParams { } } +// DefaultTestingTMConfig constructs the default Tendermint configuration for testing. func DefaultTestingTMConfig(t *testing.T, gnoroot string) *tmcfg.Config { t.Helper() @@ -120,6 +127,7 @@ func DefaultTestingTMConfig(t *testing.T, gnoroot string) *tmcfg.Config { return tmconfig } +// waitForNodeReadiness waits until the node is ready, signaling via the EventNewBlock event. // XXX: This should be replace by https://github.com/gnolang/gno/pull/1216 func waitForNodeReadiness(n *node.Node) <-chan struct{} { const listenerID = "first_block_listener" From 2f8bc5070ffa8ca89c93902214279232538c1410 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Sat, 21 Oct 2023 14:55:37 -0400 Subject: [PATCH 19/38] fix: update InMemoryNodeConfig Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/pkg/gnoland/node_inmemory.go | 124 +++++++++--------- gno.land/pkg/gnoland/types.go | 13 ++ .../pkg/integration/testing_integration.go | 8 +- gno.land/pkg/integration/testing_node.go | 81 ++++++++---- 4 files changed, 135 insertions(+), 91 deletions(-) diff --git a/gno.land/pkg/gnoland/node_inmemory.go b/gno.land/pkg/gnoland/node_inmemory.go index bb677188fee..f8d6a924876 100644 --- a/gno.land/pkg/gnoland/node_inmemory.go +++ b/gno.land/pkg/gnoland/node_inmemory.go @@ -9,28 +9,31 @@ import ( "github.com/gnolang/gno/tm2/pkg/bft/node" "github.com/gnolang/gno/tm2/pkg/bft/proxy" bft "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/crypto/ed25519" "github.com/gnolang/gno/tm2/pkg/db" "github.com/gnolang/gno/tm2/pkg/log" "github.com/gnolang/gno/tm2/pkg/p2p" - "github.com/gnolang/gno/tm2/pkg/std" ) type InMemoryNodeConfig struct { + PrivValidator bft.PrivValidator + Genesis *bft.GenesisDoc TMConfig *tmcfg.Config - ConsensusParams abci.ConsensusParams - GenesisValidator []bft.GenesisValidator - Packages []PackagePath - Balances []Balance - GenesisTXs []std.Tx SkipFailingGenesisTxs bool GenesisMaxVMCycles int64 } +// NewMockedPrivValidator generate a new key +func NewMockedPrivValidator() bft.PrivValidator { + return bft.NewMockPVWithParams(ed25519.GenPrivKey(), false, false) +} + // NewInMemoryNodeConfig creates a default configuration for an in-memory node. -func NewInMemoryNodeConfig(tmcfg *tmcfg.Config) *InMemoryNodeConfig { - return &InMemoryNodeConfig{ - TMConfig: tmcfg, +func NewDefaultGenesisConfig(pk crypto.PubKey, chainid string) *bft.GenesisDoc { + return &bft.GenesisDoc{ + GenesisTime: time.Now(), + ChainID: chainid, ConsensusParams: abci.ConsensusParams{ Block: &abci.BlockParams{ MaxTxBytes: 1_000_000, // 1MB, @@ -39,53 +42,61 @@ func NewInMemoryNodeConfig(tmcfg *tmcfg.Config) *InMemoryNodeConfig { TimeIotaMS: 100, // 100ms }, }, - GenesisMaxVMCycles: 10_000_000, } } -// NewInMemoryNode creates an in-memory gnoland node. In this mode, the node does not -// persist any data and uses an in-memory database. The `InMemoryNodeConfig.TMConfig.RootDir` -// should point to the correct gno repository to load the stdlibs. -func NewInMemoryNode(logger log.Logger, cfg *InMemoryNodeConfig) (*node.Node, error) { - if cfg.TMConfig == nil { - return nil, fmt.Errorf("no `TMConfig` given") +func NewDefaultTMConfig(rootdir string) *tmcfg.Config { + return tmcfg.DefaultConfig().SetRootDir(rootdir) +} + +// NewInMemoryNodeConfig creates a default configuration for an in-memory node. +func NewDefaultInMemoryNodeConfig(rootdir string) *InMemoryNodeConfig { + tm := NewDefaultTMConfig(rootdir) + + // Create Mocked Identity + pv := NewMockedPrivValidator() + genesis := NewDefaultGenesisConfig(pv.GetPubKey(), tm.ChainID()) + + self := pv.GetPubKey() + genesis.Validators = []bft.GenesisValidator{ + { + Address: self.Address(), + PubKey: self, + Power: 10, + Name: "self", + }, } - if cfg.TMConfig.RootDir == "" { - return nil, fmt.Errorf("`TMConfig.RootDir` is required but not provided") + return &InMemoryNodeConfig{ + PrivValidator: pv, + TMConfig: tm, + Genesis: genesis, + GenesisMaxVMCycles: 10_000_000, } +} - // Create Identity - nodekey := &p2p.NodeKey{PrivKey: ed25519.GenPrivKey()} - pv := bft.NewMockPVWithParams(ed25519.GenPrivKey(), false, false) - - // Set up genesis with default values and additional validators - gen := &bft.GenesisDoc{ - GenesisTime: time.Now(), - ChainID: cfg.TMConfig.ChainID(), - ConsensusParams: cfg.ConsensusParams, - Validators: []bft.GenesisValidator{ - { - Address: pv.GetPubKey().Address(), - PubKey: pv.GetPubKey(), - Power: 10, - Name: "self", - }, - }, +func (cfg *InMemoryNodeConfig) validate() error { + if cfg.PrivValidator == nil { + return fmt.Errorf("`PrivValidator` is required but not provided") } - gen.Validators = append(gen.Validators, cfg.GenesisValidator...) - // XXX: Maybe let the user do this manually and pass it to genesisTXs - txs, err := LoadPackages(cfg.Packages) - if err != nil { - return nil, fmt.Errorf("error loading genesis packages: %w", err) + if cfg.TMConfig == nil { + return fmt.Errorf("`TMConfig` is required but not provided") } - // Combine loaded packages with provided genesis transactions - txs = append(txs, cfg.GenesisTXs...) - gen.AppState = GnoGenesisState{ - Balances: cfg.Balances, - Txs: txs, + if cfg.TMConfig.RootDir == "" { + return fmt.Errorf("`TMConfig.RootDir` is required to locate `stdlibs` directory") + } + + return nil +} + +// NewInMemoryNode creates an in-memory gnoland node. In this mode, the node does not +// persist any data and uses an in-memory database. The `InMemoryNodeConfig.TMConfig.RootDir` +// should point to the correct gno repository to load the stdlibs. +func NewInMemoryNode(logger log.Logger, cfg *InMemoryNodeConfig) (*node.Node, error) { + if err := cfg.validate(); err != nil { + return nil, fmt.Errorf("validate config error: %w", err) } // Initialize the application with the provided options @@ -112,28 +123,19 @@ func NewInMemoryNode(logger log.Logger, cfg *InMemoryNodeConfig) (*node.Node, er // Create genesis factory genProvider := func() (*bft.GenesisDoc, error) { - return gen, nil + return cfg.Genesis, nil } + // generate p2p node identity + // XXX: do we need to configur + nodekey := &p2p.NodeKey{PrivKey: ed25519.GenPrivKey()} + // Create and return the in-memory node instance return node.NewNode(cfg.TMConfig, - pv, nodekey, + cfg.PrivValidator, nodekey, appClientCreator, genProvider, node.DefaultDBProvider, - logger + logger, ) } - -// LoadPackages loads and returns transactions from provided package paths. -func LoadPackages(pkgs []PackagePath) ([]std.Tx, error) { - var txs []std.Tx - for _, pkg := range pkgs { - tx, err := pkg.Load() - if err != nil { - return nil, fmt.Errorf("error loading package from path %s: %w", pkg.Path, err) - } - txs = append(txs, tx...) - } - return txs, nil -} diff --git a/gno.land/pkg/gnoland/types.go b/gno.land/pkg/gnoland/types.go index 3101e5d7d40..7ea199cfb39 100644 --- a/gno.land/pkg/gnoland/types.go +++ b/gno.land/pkg/gnoland/types.go @@ -117,3 +117,16 @@ func (p PackagePath) Load() ([]std.Tx, error) { return txs, nil } + +// LoadPackages loads and returns transactions from provided package paths. +func LoadPackages(pkgs []PackagePath) ([]std.Tx, error) { + var txs []std.Tx + for _, pkg := range pkgs { + tx, err := pkg.Load() + if err != nil { + return nil, fmt.Errorf("error loading package from path %s: %w", pkg.Path, err) + } + txs = append(txs, tx...) + } + return txs, nil +} diff --git a/gno.land/pkg/integration/testing_integration.go b/gno.land/pkg/integration/testing_integration.go index 004bd9260da..9a161f9fe4e 100644 --- a/gno.land/pkg/integration/testing_integration.go +++ b/gno.land/pkg/integration/testing_integration.go @@ -92,10 +92,10 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { } // Setup "test1" default account - kb.CreateAccount(DefaultAccountName, DefaultAccountSeed, "", "", 0, 0) + kb.CreateAccount(DefaultAccount_Name,DefaultAccount_Seedd, "", "", 0, 0) - env.Setenv("USER_SEED_"+DefaultAccountName, DefaultAccountSeed) - env.Setenv("USER_ADDR_"+DefaultAccountName, DefaultAccountAddress) + env.Setenv("USER_SEED_"+DefaultAccount_Name,DefaultAccount_Seedd) + env.Setenv("USER_ADDR_"+DefaultAccount_Name,DefaultAccount_Addresss) env.Setenv("GNOROOT", gnoRootDir) env.Setenv("GNOHOME", gnoHomeDir) @@ -138,7 +138,7 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { // Setup balance for default account nodecfg.Balances = append(nodecfg.Balances, gnoland.Balance{ - Address: crypto.MustAddressFromString(DefaultAccountAddress), + Address: crypto.MustAddressFromString(DefaultAccount_Address), Value: std.MustParseCoins("10000000000000ugnot"), }) diff --git a/gno.land/pkg/integration/testing_node.go b/gno.land/pkg/integration/testing_node.go index 80de8ecc811..0c1938db6c1 100644 --- a/gno.land/pkg/integration/testing_node.go +++ b/gno.land/pkg/integration/testing_node.go @@ -19,9 +19,9 @@ import ( ) const ( - DefaultAccountName = "test1" - DefaultAccountAddress = "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5" - DefaultAccountSeed = "source bonus chronic canvas draft south burst lottery vacant surface solve popular case indicate oppose farm nothing bullet exhibit title speed wink action roast" + DefaultAccount_Name = "test1" + DefaultAccount_Address = "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5" + DefaultAccount_Seed = "source bonus chronic canvas draft south burst lottery vacant surface solve popular case indicate oppose farm nothing bullet exhibit title speed wink action roast" ) // TestingInMemoryNode initializes and starts an in-memory node for testing. @@ -49,28 +49,71 @@ func DefaultTestingNodeConfig(t *testing.T, gnoroot string) *gnoland.InMemoryNod t.Helper() tmconfig := DefaultTestingTMConfig(t, gnoroot) + + // Create Mocked Identity + pv := gnoland.NewMockedPrivValidator() + + // Generate genesis config + genesis := DefaultTestingGenesisConfig(t, gnoroot, pv.GetPubKey(), tmconfig) + return &gnoland.InMemoryNodeConfig{ - Balances: LoadDefaultGenesisBalanceFile(t, gnoroot), - GenesisTXs: LoadDefaultGenesisTXsFile(t, tmconfig.ChainID(), gnoroot), - ConsensusParams: DefaultConsensusParams(t), - TMConfig: tmconfig, - Packages: LoadDefaultPackages(t, crypto.MustAddressFromString(DefaultAccountAddress), gnoroot), + PrivValidator: pv, + Genesis: genesis, + TMConfig: tmconfig, + } +} + +func DefaultTestingGenesisConfig(t *testing.T, gnoroot string, self crypto.PubKey, tmconfig *tmcfg.Config) *bft.GenesisDoc { + pkgCreator := crypto.MustAddressFromString(DefaultAccount_Address) // test1 + + // Load genesis packages + genesisPackagesTxs := LoadDefaultPackages(t, pkgCreator, gnoroot) + + return &bft.GenesisDoc{ + GenesisTime: time.Now(), + ChainID: tmconfig.ChainID(), + ConsensusParams: abci.ConsensusParams{ + Block: &abci.BlockParams{ + MaxTxBytes: 1_000_000, // 1MB, + MaxDataBytes: 2_000_000, // 2MB, + MaxGas: 10_0000_000, // 10M gas + TimeIotaMS: 100, // 100ms + }, + }, + Validators: []bft.GenesisValidator{ + { + Address: self.Address(), + PubKey: self, + Power: 10, + Name: "self", + }, + }, + AppState: gnoland.GnoGenesisState{ + Balances: []gnoland.Balance{ + { + Address: pkgCreator, + Value: std.MustParseCoins("10000000000000ugnot"), + }, + }, + Txs: genesisPackagesTxs, + }, } } // LoadDefaultPackages loads the default packages for testing using a given creator address and gnoroot directory. -func LoadDefaultPackages(t *testing.T, creator bft.Address, gnoroot string) []gnoland.PackagePath { +func LoadDefaultPackages(t *testing.T, creator bft.Address, gnoroot string) []std.Tx { t.Helper() exampleDir := filepath.Join(gnoroot, "examples") - pkgs := gnoland.PackagePath{ + txs, err := gnoland.LoadPackages(gnoland.PackagePath{ Creator: creator, Fee: std.NewFee(50000, std.MustParseCoin("1000000ugnot")), Path: exampleDir, - } + }) + require.NoError(t, err) - return []gnoland.PackagePath{pkgs} + return txs } // LoadDefaultGenesisBalanceFile loads the default genesis balance file for testing. @@ -99,20 +142,6 @@ func LoadDefaultGenesisTXsFile(t *testing.T, chainid string, gnoroot string) []s return genesisTXs } -// DefaultConsensusParams constructs the default consensus parameters for testing. -func DefaultConsensusParams(t *testing.T) abci.ConsensusParams { - t.Helper() - - return abci.ConsensusParams{ - Block: &abci.BlockParams{ - MaxTxBytes: 1_000_000, // 1MB, - MaxDataBytes: 2_000_000, // 2MB, - MaxGas: 10_000_000, // 10M gas - TimeIotaMS: 100, // 100ms - }, - } -} - // DefaultTestingTMConfig constructs the default Tendermint configuration for testing. func DefaultTestingTMConfig(t *testing.T, gnoroot string) *tmcfg.Config { t.Helper() From ff0aef2aae65e97b35bde9c84fff28a08cd38e4c Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Wed, 25 Oct 2023 18:52:07 -0400 Subject: [PATCH 20/38] chore: fix & lint Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/pkg/gnoland/node_inmemory.go | 8 +- gno.land/pkg/gnoland/types.go | 2 +- gno.land/pkg/integration/testing.go | 39 ++++++++++ .../pkg/integration/testing_integration.go | 75 +++++++------------ gno.land/pkg/integration/testing_node.go | 29 ++----- 5 files changed, 82 insertions(+), 71 deletions(-) create mode 100644 gno.land/pkg/integration/testing.go diff --git a/gno.land/pkg/gnoland/node_inmemory.go b/gno.land/pkg/gnoland/node_inmemory.go index f8d6a924876..a0ab6a51e82 100644 --- a/gno.land/pkg/gnoland/node_inmemory.go +++ b/gno.land/pkg/gnoland/node_inmemory.go @@ -14,10 +14,11 @@ import ( "github.com/gnolang/gno/tm2/pkg/db" "github.com/gnolang/gno/tm2/pkg/log" "github.com/gnolang/gno/tm2/pkg/p2p" + "github.com/gnolang/gno/tm2/pkg/std" ) type InMemoryNodeConfig struct { - PrivValidator bft.PrivValidator + PrivValidator bft.PrivValidator // identity of the validator Genesis *bft.GenesisDoc TMConfig *tmcfg.Config SkipFailingGenesisTxs bool @@ -42,6 +43,10 @@ func NewDefaultGenesisConfig(pk crypto.PubKey, chainid string) *bft.GenesisDoc { TimeIotaMS: 100, // 100ms }, }, + AppState: &GnoGenesisState{ + Balances: []Balance{}, + Txs: []std.Tx{}, + }, } } @@ -57,6 +62,7 @@ func NewDefaultInMemoryNodeConfig(rootdir string) *InMemoryNodeConfig { pv := NewMockedPrivValidator() genesis := NewDefaultGenesisConfig(pv.GetPubKey(), tm.ChainID()) + // Add self as validator self := pv.GetPubKey() genesis.Validators = []bft.GenesisValidator{ { diff --git a/gno.land/pkg/gnoland/types.go b/gno.land/pkg/gnoland/types.go index 7ea199cfb39..8830251cb0d 100644 --- a/gno.land/pkg/gnoland/types.go +++ b/gno.land/pkg/gnoland/types.go @@ -119,7 +119,7 @@ func (p PackagePath) Load() ([]std.Tx, error) { } // LoadPackages loads and returns transactions from provided package paths. -func LoadPackages(pkgs []PackagePath) ([]std.Tx, error) { +func LoadPackages(pkgs ...PackagePath) ([]std.Tx, error) { var txs []std.Tx for _, pkg := range pkgs { tx, err := pkg.Load() diff --git a/gno.land/pkg/integration/testing.go b/gno.land/pkg/integration/testing.go new file mode 100644 index 00000000000..7803e213da1 --- /dev/null +++ b/gno.land/pkg/integration/testing.go @@ -0,0 +1,39 @@ +package integration + +import ( + "errors" + + "github.com/jaekwon/testify/assert" + "github.com/jaekwon/testify/require" + "github.com/rogpeppe/go-internal/testscript" +) + +// This error is from testscript.Fatalf and is needed to correctly +// handle the FailNow method. +// see: https://github.com/rogpeppe/go-internal/blob/32ae33786eccde1672d4ba373c80e1bc282bfbf6/testscript/testscript.go#L799-L812 +var errFailNow = errors.New("fail now!") //nolint:stylecheck + +var ( + _ require.TestingT = (*testingTS)(nil) + _ assert.TestingT = (*testingTS)(nil) +) + +type TestingTS = require.TestingT + +type testingTS struct { + *testscript.TestScript +} + +func TSTestingT(ts *testscript.TestScript) TestingTS { + return &testingTS{ts} +} + +func (t *testingTS) Errorf(format string, args ...interface{}) { + defer recover() // we can ignore recover result, we just want to catch it up + t.Fatalf(format, args...) +} + +func (t *testingTS) FailNow() { + // unfortunately we can't access underlying `t.t.FailNow` method + panic(errFailNow) +} diff --git a/gno.land/pkg/integration/testing_integration.go b/gno.land/pkg/integration/testing_integration.go index 9a161f9fe4e..d37a900cf96 100644 --- a/gno.land/pkg/integration/testing_integration.go +++ b/gno.land/pkg/integration/testing_integration.go @@ -2,7 +2,6 @@ package integration import ( "context" - "errors" "fmt" "hash/crc32" "os" @@ -11,17 +10,13 @@ import ( "strings" "sync" "testing" - "time" "github.com/gnolang/gno/gno.land/pkg/gnoland" - "github.com/gnolang/gno/tm2/pkg/bft/config" "github.com/gnolang/gno/tm2/pkg/bft/node" "github.com/gnolang/gno/tm2/pkg/commands" - "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/crypto/keys" "github.com/gnolang/gno/tm2/pkg/crypto/keys/client" "github.com/gnolang/gno/tm2/pkg/log" - "github.com/gnolang/gno/tm2/pkg/std" "github.com/rogpeppe/go-internal/testscript" ) @@ -92,10 +87,10 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { } // Setup "test1" default account - kb.CreateAccount(DefaultAccount_Name,DefaultAccount_Seedd, "", "", 0, 0) + kb.CreateAccount(DefaultAccount_Name, DefaultAccount_Seed, "", "", 0, 0) - env.Setenv("USER_SEED_"+DefaultAccount_Name,DefaultAccount_Seedd) - env.Setenv("USER_ADDR_"+DefaultAccount_Name,DefaultAccount_Addresss) + env.Setenv("USER_SEED_"+DefaultAccount_Name, DefaultAccount_Seed) + env.Setenv("USER_ADDR_"+DefaultAccount_Name, DefaultAccount_Address) env.Setenv("GNOROOT", gnoRootDir) env.Setenv("GNOHOME", gnoHomeDir) @@ -126,33 +121,17 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { break } - // Generate node config - cfg := config.TestConfig().SetRootDir(gnoRootDir) + // Warp up `ts` so we can pass it to other testing method + t := TSTestingT(ts) - // Use random port be able to run test in parallel without conflict - cfg.P2P.ListenAddress = "tcp://127.0.0.1:0" - cfg.RPC.ListenAddress = "tcp://127.0.0.1:0" + // Generate config and node + cfg := DefaultTestingNodeConfig(t, gnoRootDir) + n, remoteAddr := TestingInMemoryNode(t, logger, cfg) - // Create the Node - nodecfg := gnoland.NewInMemoryNodeConfig(cfg) - - // Setup balance for default account - nodecfg.Balances = append(nodecfg.Balances, gnoland.Balance{ - Address: crypto.MustAddressFromString(DefaultAccount_Address), - Value: std.MustParseCoins("10000000000000ugnot"), - }) - - var n *node.Node - if n, err = execInMemoryGnoland(ts, logger, nodecfg); err != nil { - break - } - - // Register cleanup. + // Register cleanup nodes[sid] = &testNode{Node: n} - remoteAddr := n.Config().RPC.ListenAddress - - // Add default environements. + // Add default environements ts.Setenv("RPC_ADDR", remoteAddr) fmt.Fprintln(ts.Stdout(), "node started successfully") @@ -166,7 +145,7 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { if err = n.Stop(); err == nil { delete(nodes, sid) - // Unset gnoland environements. + // Unset gnoland environements ts.Setenv("RPC_ADDR", "") fmt.Fprintln(ts.Stdout(), "node stopped successfully") } @@ -183,7 +162,7 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { logger := ts.Value("_logger").(log.Logger) // grab logger sid := ts.Getenv("SID") // grab session id - // Setup IO command. + // Setup IO command io := commands.NewTestIO() io.SetOut(commands.WriteNopCloser(ts.Stdout())) io.SetErr(commands.WriteNopCloser(ts.Stderr())) @@ -221,24 +200,24 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { } } -func execInMemoryGnoland(ts *testscript.TestScript, logger log.Logger, nodecfg *gnoland.InMemoryNodeConfig) (*node.Node, error) { - n, err := gnoland.NewInMemoryNode(logger, nodecfg) - if err != nil { - return nil, fmt.Errorf("unable to create node: %w", err) - } +// func execInMemoryGnoland(ts *testscript.TestScript, logger log.Logger, nodecfg *gnoland.InMemoryNodeConfig) (*node.Node, error) { +// n, err := gnoland.NewInMemoryNode(logger, nodecfg) +// if err != nil { +// return nil, fmt.Errorf("unable to create node: %w", err) +// } - if err = n.Start(); err != nil { - return nil, fmt.Errorf("unable to start node: %w", err) - } +// if err = n.Start(); err != nil { +// return nil, fmt.Errorf("unable to start node: %w", err) +// } - select { - case <-time.After(time.Second * 6): - return nil, errors.New("timeout while waiting for the node to start") - case <-waitForNodeReadiness(n): // ok - } +// select { +// case <-time.After(time.Second * 6): +// return nil, errors.New("timeout while waiting for the node to start") +// case <-waitForNodeReadiness(n): // ok +// } - return n, nil -} +// return n, nil +// } func getTestingLogger(env *testscript.Env, logname string) (log.Logger, error) { var path string diff --git a/gno.land/pkg/integration/testing_node.go b/gno.land/pkg/integration/testing_node.go index 0c1938db6c1..9e20790ba76 100644 --- a/gno.land/pkg/integration/testing_node.go +++ b/gno.land/pkg/integration/testing_node.go @@ -3,7 +3,6 @@ package integration import ( "path/filepath" "sync" - "testing" "time" "github.com/gnolang/gno/gno.land/pkg/gnoland" @@ -26,9 +25,7 @@ const ( // TestingInMemoryNode initializes and starts an in-memory node for testing. // It returns the node instance and its RPC remote address. -func TestingInMemoryNode(t *testing.T, logger log.Logger, config *gnoland.InMemoryNodeConfig) (*node.Node, string) { - t.Helper() - +func TestingInMemoryNode(t TestingTS, logger log.Logger, config *gnoland.InMemoryNodeConfig) (*node.Node, string) { node, err := gnoland.NewInMemoryNode(logger, config) require.NoError(t, err) @@ -45,10 +42,8 @@ func TestingInMemoryNode(t *testing.T, logger log.Logger, config *gnoland.InMemo } // DefaultTestingNodeConfig constructs the default in-memory node configuration for testing. -func DefaultTestingNodeConfig(t *testing.T, gnoroot string) *gnoland.InMemoryNodeConfig { - t.Helper() - - tmconfig := DefaultTestingTMConfig(t, gnoroot) +func DefaultTestingNodeConfig(t TestingTS, gnoroot string) *gnoland.InMemoryNodeConfig { + tmconfig := DefaultTestingTMConfig(gnoroot) // Create Mocked Identity pv := gnoland.NewMockedPrivValidator() @@ -63,7 +58,7 @@ func DefaultTestingNodeConfig(t *testing.T, gnoroot string) *gnoland.InMemoryNod } } -func DefaultTestingGenesisConfig(t *testing.T, gnoroot string, self crypto.PubKey, tmconfig *tmcfg.Config) *bft.GenesisDoc { +func DefaultTestingGenesisConfig(t TestingTS, gnoroot string, self crypto.PubKey, tmconfig *tmcfg.Config) *bft.GenesisDoc { pkgCreator := crypto.MustAddressFromString(DefaultAccount_Address) // test1 // Load genesis packages @@ -101,9 +96,7 @@ func DefaultTestingGenesisConfig(t *testing.T, gnoroot string, self crypto.PubKe } // LoadDefaultPackages loads the default packages for testing using a given creator address and gnoroot directory. -func LoadDefaultPackages(t *testing.T, creator bft.Address, gnoroot string) []std.Tx { - t.Helper() - +func LoadDefaultPackages(t TestingTS, creator bft.Address, gnoroot string) []std.Tx { exampleDir := filepath.Join(gnoroot, "examples") txs, err := gnoland.LoadPackages(gnoland.PackagePath{ @@ -117,9 +110,7 @@ func LoadDefaultPackages(t *testing.T, creator bft.Address, gnoroot string) []st } // LoadDefaultGenesisBalanceFile loads the default genesis balance file for testing. -func LoadDefaultGenesisBalanceFile(t *testing.T, gnoroot string) []gnoland.Balance { - t.Helper() - +func LoadDefaultGenesisBalanceFile(t TestingTS, gnoroot string) []gnoland.Balance { balanceFile := filepath.Join(gnoroot, "gno.land", "genesis", "genesis_balances.txt") genesisBalances, err := gnoland.LoadGenesisBalancesFile(balanceFile) @@ -129,9 +120,7 @@ func LoadDefaultGenesisBalanceFile(t *testing.T, gnoroot string) []gnoland.Balan } // LoadDefaultGenesisTXsFile loads the default genesis transactions file for testing. -func LoadDefaultGenesisTXsFile(t *testing.T, chainid string, gnoroot string) []std.Tx { - t.Helper() - +func LoadDefaultGenesisTXsFile(t TestingTS, chainid string, gnoroot string) []std.Tx { txsFile := filepath.Join(gnoroot, "gno.land", "genesis", "genesis_txs.txt") // NOTE: We dont care about giving a correct address here, as it's only for display @@ -143,9 +132,7 @@ func LoadDefaultGenesisTXsFile(t *testing.T, chainid string, gnoroot string) []s } // DefaultTestingTMConfig constructs the default Tendermint configuration for testing. -func DefaultTestingTMConfig(t *testing.T, gnoroot string) *tmcfg.Config { - t.Helper() - +func DefaultTestingTMConfig(gnoroot string) *tmcfg.Config { const defaultListner = "tcp://127.0.0.1:0" tmconfig := tmcfg.TestConfig().SetRootDir(gnoroot) From 3584d866de6aab2bb810e6c46ad1e119d49cacef Mon Sep 17 00:00:00 2001 From: Guilhem Fanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 26 Oct 2023 08:45:32 -0400 Subject: [PATCH 21/38] Update gno.land/cmd/gnoland/start.go Co-authored-by: Manfred Touron <94029+moul@users.noreply.github.com> --- gno.land/cmd/gnoland/start.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gno.land/cmd/gnoland/start.go b/gno.land/cmd/gnoland/start.go index 0e6bc6c06c7..938d4f9b4f3 100644 --- a/gno.land/cmd/gnoland/start.go +++ b/gno.land/cmd/gnoland/start.go @@ -60,7 +60,7 @@ func newStartCmd(io *commands.IO) *commands.Command { func (c *startCfg) RegisterFlags(fs *flag.FlagSet) { gnoroot := gnoland.MustGuessGnoRootDir() - defaultGenesisBalancesFile := filepath.Join(gnoroot, "gno.land/genesis/genesis_balances.txt") + defaultGenesisBalancesFile := filepath.Join(gnoroot, "gno.land", "genesis", "genesis_balances.txt") defaultGenesisTxsFile := filepath.Join(gnoroot, "gno.land/genesis/genesis_txs.txt") fs.BoolVar( From a580f54ae02c21c08d3f61c2cb911ca8de410623 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Fri, 27 Oct 2023 10:09:32 -0400 Subject: [PATCH 22/38] chore: cleanup Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/cmd/gnoland/start.go | 13 ++-- gno.land/cmd/gnoweb/main_test.go | 34 +++++++-- gno.land/pkg/gnoland/genesis.go | 47 ++++++++++++ gno.land/pkg/gnoland/types.go | 71 ------------------- .../pkg/integration/testing_integration.go | 19 ----- gno.land/pkg/integration/testing_node.go | 10 ++- 6 files changed, 85 insertions(+), 109 deletions(-) diff --git a/gno.land/cmd/gnoland/start.go b/gno.land/cmd/gnoland/start.go index 938d4f9b4f3..64164c828ac 100644 --- a/gno.land/cmd/gnoland/start.go +++ b/gno.land/cmd/gnoland/start.go @@ -198,6 +198,7 @@ func execStart(c *startCfg, io *commands.IO) error { // Write genesis file if missing. genesisFilePath := filepath.Join(dataDir, cfg.Genesis) + if !osm.FileExists(genesisFilePath) { // Create priv validator first. // Need it to generate genesis.json @@ -231,6 +232,8 @@ func execStart(c *startCfg, io *commands.IO) error { return fmt.Errorf("error in creating node: %w", err) } + fmt.Fprintln(io.Err, "Node created.") + if c.skipStart { io.ErrPrintln("'--skip-start' is set. Exiting.") return nil @@ -282,14 +285,8 @@ func generateGenesisFile(genesisFile string, pk crypto.PubKey, c *startCfg) erro // Load examples folder examplesDir := filepath.Join(c.gnoRootDir, "examples") test1 := crypto.MustAddressFromString("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") - examplePkgs := gnoland.PackagePath{ - Path: examplesDir, - Creator: test1, - Fee: std.NewFee(50000, std.MustParseCoin("1000000ugnot")), - Deposit: nil, - } - - pkgsTxs, err := examplePkgs.Load() + defaultFee := std.NewFee(50000, std.MustParseCoin("1000000ugnot")) + pkgsTxs, err := gnoland.LoadPackagesFromDir(examplesDir, test1, defaultFee, nil) if err != nil { return fmt.Errorf("unable to load examples folder: %w", err) } diff --git a/gno.land/cmd/gnoweb/main_test.go b/gno.land/cmd/gnoweb/main_test.go index dd7b28d72b2..e033a3b9014 100644 --- a/gno.land/cmd/gnoweb/main_test.go +++ b/gno.land/cmd/gnoweb/main_test.go @@ -5,6 +5,7 @@ import ( "net/http" "net/http/httptest" "os" + "path/filepath" "strings" "testing" @@ -12,6 +13,7 @@ import ( "github.com/gnolang/gno/gno.land/pkg/integration" "github.com/gnolang/gno/tm2/pkg/log" "github.com/gotuna/gotuna/test/assert" + "github.com/jaekwon/testify/require" ) func TestRoutes(t *testing.T) { @@ -44,12 +46,20 @@ func TestRoutes(t *testing.T) { {"/blog", found, "/r/gnoland/blog"}, {"/404-not-found", notFound, "/404-not-found"}, } - if wd, err := os.Getwd(); err == nil { - if strings.HasSuffix(wd, "cmd/gnoweb") { - os.Chdir("../..") + + // XXX: The following block is really bad and should be Replace by testscripts tests + { + if wd, err := os.Getwd(); err == nil { + if strings.HasSuffix(wd, "cmd/gnoweb") { + + currentPwd, err := filepath.Abs(".") + require.NoError(t, err) + os.Chdir("../..") + defer os.Chdir(currentPwd) + } + } else { + panic("os.Getwd() -> err: " + err.Error()) } - } else { - panic("os.Getwd() -> err: " + err.Error()) } config := integration.DefaultTestingNodeConfig(t, gnoland.MustGuessGnoRootDir()) @@ -96,9 +106,22 @@ func TestAnalytics(t *testing.T) { "/404-not-found", } + config := integration.DefaultTestingNodeConfig(t, gnoland.MustGuessGnoRootDir()) + node, remoteAddr := integration.TestingInMemoryNode(t, log.NewNopLogger(), config) + defer node.Stop() + + // XXX: The following block is really bad and should be Replace by testscripts tests + { + currentPwd, err := filepath.Abs("../..") + require.NoError(t, err) + os.Chdir("../..") + defer os.Chdir(currentPwd) + } + t.Run("with", func(t *testing.T) { for _, route := range routes { t.Run(route, func(t *testing.T) { + flags.RemoteAddr = remoteAddr flags.WithAnalytics = true app := makeApp() request := httptest.NewRequest(http.MethodGet, route, nil) @@ -111,6 +134,7 @@ func TestAnalytics(t *testing.T) { t.Run("without", func(t *testing.T) { for _, route := range routes { t.Run(route, func(t *testing.T) { + flags.RemoteAddr = remoteAddr flags.WithAnalytics = false app := makeApp() request := httptest.NewRequest(http.MethodGet, route, nil) diff --git a/gno.land/pkg/gnoland/genesis.go b/gno.land/pkg/gnoland/genesis.go index 9b527590ae7..94c9d6cbf03 100644 --- a/gno.land/pkg/gnoland/genesis.go +++ b/gno.land/pkg/gnoland/genesis.go @@ -5,12 +5,17 @@ import ( "fmt" "strings" + vmm "github.com/gnolang/gno/gno.land/pkg/sdk/vm" + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/gnovm/pkg/gnomod" "github.com/gnolang/gno/tm2/pkg/amino" + bft "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/crypto" osm "github.com/gnolang/gno/tm2/pkg/os" "github.com/gnolang/gno/tm2/pkg/std" ) +// LoadGenesisBalancesFile loads genesis balances from the provided file path. func LoadGenesisBalancesFile(path string) ([]Balance, error) { // each balance is in the form: g1xxxxxxxxxxxxxxxx=100000ugnot content := osm.MustReadFile(path) @@ -53,6 +58,7 @@ func LoadGenesisBalancesFile(path string) ([]Balance, error) { return balances, nil } +// LoadGenesisTxsFile loads genesis transactions from the provided file path. // XXX: Improve the way we generate and load this file func LoadGenesisTxsFile(path string, chainID string, genesisRemote string) ([]std.Tx, error) { txs := []std.Tx{} @@ -77,3 +83,44 @@ func LoadGenesisTxsFile(path string, chainID string, genesisRemote string) ([]st return txs, nil } + +// LoadPackagesFromDir loads gno packages from a directory. +// It creates and returns a list of transactions based on these packages. +func LoadPackagesFromDir(dir string, creator bft.Address, fee std.Fee, deposit std.Coins) ([]std.Tx, error) { + // list all packages from target path + pkgs, err := gnomod.ListPkgs(dir) + if err != nil { + return nil, fmt.Errorf("listing gno packages: %w", err) + } + + // Sort packages by dependencies. + sortedPkgs, err := pkgs.Sort() + if err != nil { + return nil, fmt.Errorf("sorting packages: %w", err) + } + + // Filter out draft packages. + nonDraftPkgs := sortedPkgs.GetNonDraftPkgs() + txs := []std.Tx{} + for _, pkg := range nonDraftPkgs { + // Open files in directory as MemPackage. + memPkg := gno.ReadMemPackage(pkg.Dir, pkg.Name) + + // Create transaction + tx := std.Tx{ + Fee: fee, + Msgs: []std.Msg{ + vmm.MsgAddPackage{ + Creator: creator, + Package: memPkg, + Deposit: deposit, + }, + }, + } + + tx.Signatures = make([]std.Signature, len(tx.GetSigners())) + txs = append(txs, tx) + } + + return txs, nil +} diff --git a/gno.land/pkg/gnoland/types.go b/gno.land/pkg/gnoland/types.go index 8830251cb0d..7dba82bc6a3 100644 --- a/gno.land/pkg/gnoland/types.go +++ b/gno.land/pkg/gnoland/types.go @@ -1,13 +1,9 @@ package gnoland import ( - "errors" "fmt" "strings" - vmm "github.com/gnolang/gno/gno.land/pkg/sdk/vm" - gno "github.com/gnolang/gno/gnovm/pkg/gnolang" - "github.com/gnolang/gno/gnovm/pkg/gnomod" bft "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/std" @@ -63,70 +59,3 @@ func (b *Balance) Marshaljson() ([]byte, error) { func (b Balance) String() string { return fmt.Sprintf("%s=%s", b.Address.String(), b.Value.String()) } - -type PackagePath struct { - Creator bft.Address - Deposit std.Coins - Fee std.Fee - Path string -} - -func (p PackagePath) Load() ([]std.Tx, error) { - if p.Creator.IsZero() { - return nil, errors.New("empty creator address") - } - - if p.Path == "" { - return nil, errors.New("empty package path") - } - - // list all packages from target path - pkgs, err := gnomod.ListPkgs(p.Path) - if err != nil { - return nil, fmt.Errorf("listing gno packages: %w", err) - } - - // Sort packages by dependencies. - sortedPkgs, err := pkgs.Sort() - if err != nil { - return nil, fmt.Errorf("sorting packages: %w", err) - } - - // Filter out draft packages. - nonDraftPkgs := sortedPkgs.GetNonDraftPkgs() - txs := []std.Tx{} - for _, pkg := range nonDraftPkgs { - // Open files in directory as MemPackage. - memPkg := gno.ReadMemPackage(pkg.Dir, pkg.Name) - - // Create transaction - tx := std.Tx{ - Fee: p.Fee, - Msgs: []std.Msg{ - vmm.MsgAddPackage{ - Creator: p.Creator, - Package: memPkg, - Deposit: p.Deposit, - }, - }, - } - - tx.Signatures = make([]std.Signature, len(tx.GetSigners())) - txs = append(txs, tx) - } - - return txs, nil -} - -// LoadPackages loads and returns transactions from provided package paths. -func LoadPackages(pkgs ...PackagePath) ([]std.Tx, error) { - var txs []std.Tx - for _, pkg := range pkgs { - tx, err := pkg.Load() - if err != nil { - return nil, fmt.Errorf("error loading package from path %s: %w", pkg.Path, err) - } - txs = append(txs, tx...) - } - return txs, nil -} diff --git a/gno.land/pkg/integration/testing_integration.go b/gno.land/pkg/integration/testing_integration.go index d37a900cf96..c2847c9e129 100644 --- a/gno.land/pkg/integration/testing_integration.go +++ b/gno.land/pkg/integration/testing_integration.go @@ -200,25 +200,6 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { } } -// func execInMemoryGnoland(ts *testscript.TestScript, logger log.Logger, nodecfg *gnoland.InMemoryNodeConfig) (*node.Node, error) { -// n, err := gnoland.NewInMemoryNode(logger, nodecfg) -// if err != nil { -// return nil, fmt.Errorf("unable to create node: %w", err) -// } - -// if err = n.Start(); err != nil { -// return nil, fmt.Errorf("unable to start node: %w", err) -// } - -// select { -// case <-time.After(time.Second * 6): -// return nil, errors.New("timeout while waiting for the node to start") -// case <-waitForNodeReadiness(n): // ok -// } - -// return n, nil -// } - func getTestingLogger(env *testscript.Env, logname string) (log.Logger, error) { var path string diff --git a/gno.land/pkg/integration/testing_node.go b/gno.land/pkg/integration/testing_node.go index 9e20790ba76..01d575b69ac 100644 --- a/gno.land/pkg/integration/testing_node.go +++ b/gno.land/pkg/integration/testing_node.go @@ -97,13 +97,11 @@ func DefaultTestingGenesisConfig(t TestingTS, gnoroot string, self crypto.PubKey // LoadDefaultPackages loads the default packages for testing using a given creator address and gnoroot directory. func LoadDefaultPackages(t TestingTS, creator bft.Address, gnoroot string) []std.Tx { - exampleDir := filepath.Join(gnoroot, "examples") + examplesDir := filepath.Join(gnoroot, "examples") - txs, err := gnoland.LoadPackages(gnoland.PackagePath{ - Creator: creator, - Fee: std.NewFee(50000, std.MustParseCoin("1000000ugnot")), - Path: exampleDir, - }) + defaultFee := std.NewFee(50000, std.MustParseCoin("1000000ugnot")) + defaultCreator := crypto.MustAddressFromString(DefaultAccount_Address) // test1 + txs, err := gnoland.LoadPackagesFromDir(examplesDir, defaultCreator, defaultFee, nil) require.NoError(t, err) return txs From d9bcf3537778d4d8ab04fe0e46367f47de6200ed Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Fri, 27 Oct 2023 10:35:22 -0400 Subject: [PATCH 23/38] chore: add back previous gnoweb flag Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/cmd/gnoweb/main_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gno.land/cmd/gnoweb/main_test.go b/gno.land/cmd/gnoweb/main_test.go index e033a3b9014..5a35b906234 100644 --- a/gno.land/cmd/gnoweb/main_test.go +++ b/gno.land/cmd/gnoweb/main_test.go @@ -69,6 +69,10 @@ func TestRoutes(t *testing.T) { // set the `remoteAddr` of the client to the listening address of the // node, which is randomly assigned. flags.RemoteAddr = remoteAddr + flags.HelpChainID = "dev" + flags.CaptchaSite = "" + flags.ViewsDir = "./cmd/gnoweb/views" + flags.WithAnalytics = false app := makeApp() for _, r := range routes { From daaa7a37a02d110c5476b5baff2d3ad6a8c3a388 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Fri, 27 Oct 2023 13:40:30 -0400 Subject: [PATCH 24/38] fix: fix gnoweb analytics test Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/cmd/gnoweb/main_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gno.land/cmd/gnoweb/main_test.go b/gno.land/cmd/gnoweb/main_test.go index 5a35b906234..58392e126f2 100644 --- a/gno.land/cmd/gnoweb/main_test.go +++ b/gno.land/cmd/gnoweb/main_test.go @@ -131,7 +131,7 @@ func TestAnalytics(t *testing.T) { request := httptest.NewRequest(http.MethodGet, route, nil) response := httptest.NewRecorder() app.Router.ServeHTTP(response, request) - assert.Contains(t, response.Body.String(), "simpleanalytics") + assert.Contains(t, response.Body.String(), "sa.gno.services") }) } }) @@ -144,7 +144,7 @@ func TestAnalytics(t *testing.T) { request := httptest.NewRequest(http.MethodGet, route, nil) response := httptest.NewRecorder() app.Router.ServeHTTP(response, request) - assert.Equal(t, strings.Contains(response.Body.String(), "simpleanalytics"), false) + assert.Equal(t, strings.Contains(response.Body.String(), "sa.gno.services"), false) }) } }) From 81afbf4c861408e49ca59750e1c5e0fdcf093da5 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Fri, 27 Oct 2023 14:11:12 -0400 Subject: [PATCH 25/38] chore: lint Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/cmd/gnoweb/main_test.go | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/gno.land/cmd/gnoweb/main_test.go b/gno.land/cmd/gnoweb/main_test.go index 58392e126f2..2ec45854595 100644 --- a/gno.land/cmd/gnoweb/main_test.go +++ b/gno.land/cmd/gnoweb/main_test.go @@ -47,19 +47,13 @@ func TestRoutes(t *testing.T) { {"/404-not-found", notFound, "/404-not-found"}, } - // XXX: The following block is really bad and should be Replace by testscripts tests + // XXX: The following block should be Replace by testscripts tests { - if wd, err := os.Getwd(); err == nil { - if strings.HasSuffix(wd, "cmd/gnoweb") { - - currentPwd, err := filepath.Abs(".") - require.NoError(t, err) - os.Chdir("../..") - defer os.Chdir(currentPwd) - } - } else { - panic("os.Getwd() -> err: " + err.Error()) - } + currentPwd, err := filepath.Abs(".") + require.NoError(t, err) + err = os.Chdir("../..") + require.NoError(t, err) + defer os.Chdir(currentPwd) } config := integration.DefaultTestingNodeConfig(t, gnoland.MustGuessGnoRootDir()) @@ -118,7 +112,8 @@ func TestAnalytics(t *testing.T) { { currentPwd, err := filepath.Abs("../..") require.NoError(t, err) - os.Chdir("../..") + err = os.Chdir("../..") + require.NoError(t, err) defer os.Chdir(currentPwd) } From 8df3622ab827ecfc10e0a9db9b68a57a75b18def Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Fri, 27 Oct 2023 15:33:21 -0400 Subject: [PATCH 26/38] fix: add minimal and full config Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/cmd/gnoweb/main.go | 1 + gno.land/cmd/gnoweb/main_test.go | 28 ++----------- .../pkg/integration/testing_integration.go | 2 +- gno.land/pkg/integration/testing_node.go | 39 ++++++++++++------- 4 files changed, 31 insertions(+), 39 deletions(-) diff --git a/gno.land/cmd/gnoweb/main.go b/gno.land/cmd/gnoweb/main.go index 0d9398cb8e2..83d9664bb8e 100644 --- a/gno.land/cmd/gnoweb/main.go +++ b/gno.land/cmd/gnoweb/main.go @@ -486,6 +486,7 @@ func writeError(w http.ResponseWriter, err error) { // XXX: writeError should return an error page template. w.WriteHeader(500) + panic(err) details := errors.Unwrap(err).Error() main := err.Error() diff --git a/gno.land/cmd/gnoweb/main_test.go b/gno.land/cmd/gnoweb/main_test.go index 2ec45854595..61650563405 100644 --- a/gno.land/cmd/gnoweb/main_test.go +++ b/gno.land/cmd/gnoweb/main_test.go @@ -4,8 +4,6 @@ import ( "fmt" "net/http" "net/http/httptest" - "os" - "path/filepath" "strings" "testing" @@ -13,7 +11,6 @@ import ( "github.com/gnolang/gno/gno.land/pkg/integration" "github.com/gnolang/gno/tm2/pkg/log" "github.com/gotuna/gotuna/test/assert" - "github.com/jaekwon/testify/require" ) func TestRoutes(t *testing.T) { @@ -47,16 +44,7 @@ func TestRoutes(t *testing.T) { {"/404-not-found", notFound, "/404-not-found"}, } - // XXX: The following block should be Replace by testscripts tests - { - currentPwd, err := filepath.Abs(".") - require.NoError(t, err) - err = os.Chdir("../..") - require.NoError(t, err) - defer os.Chdir(currentPwd) - } - - config := integration.DefaultTestingNodeConfig(t, gnoland.MustGuessGnoRootDir()) + config, _ := integration.TestingNodeConfig(t, gnoland.MustGuessGnoRootDir()) node, remoteAddr := integration.TestingInMemoryNode(t, log.NewNopLogger(), config) defer node.Stop() @@ -65,7 +53,7 @@ func TestRoutes(t *testing.T) { flags.RemoteAddr = remoteAddr flags.HelpChainID = "dev" flags.CaptchaSite = "" - flags.ViewsDir = "./cmd/gnoweb/views" + flags.ViewsDir = "../../cmd/gnoweb/views" flags.WithAnalytics = false app := makeApp() @@ -104,19 +92,11 @@ func TestAnalytics(t *testing.T) { "/404-not-found", } - config := integration.DefaultTestingNodeConfig(t, gnoland.MustGuessGnoRootDir()) + config, _ := integration.TestingNodeConfig(t, gnoland.MustGuessGnoRootDir()) node, remoteAddr := integration.TestingInMemoryNode(t, log.NewNopLogger(), config) defer node.Stop() - // XXX: The following block is really bad and should be Replace by testscripts tests - { - currentPwd, err := filepath.Abs("../..") - require.NoError(t, err) - err = os.Chdir("../..") - require.NoError(t, err) - defer os.Chdir(currentPwd) - } - + flags.ViewsDir = "../../cmd/gnoweb/views" t.Run("with", func(t *testing.T) { for _, route := range routes { t.Run(route, func(t *testing.T) { diff --git a/gno.land/pkg/integration/testing_integration.go b/gno.land/pkg/integration/testing_integration.go index c2847c9e129..b773317513f 100644 --- a/gno.land/pkg/integration/testing_integration.go +++ b/gno.land/pkg/integration/testing_integration.go @@ -125,7 +125,7 @@ func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { t := TSTestingT(ts) // Generate config and node - cfg := DefaultTestingNodeConfig(t, gnoRootDir) + cfg := TestingMinimalNodeConfig(t, gnoRootDir) n, remoteAddr := TestingInMemoryNode(t, logger, cfg) // Register cleanup diff --git a/gno.land/pkg/integration/testing_node.go b/gno.land/pkg/integration/testing_node.go index 01d575b69ac..93fb16f683c 100644 --- a/gno.land/pkg/integration/testing_node.go +++ b/gno.land/pkg/integration/testing_node.go @@ -41,8 +41,29 @@ func TestingInMemoryNode(t TestingTS, logger log.Logger, config *gnoland.InMemor return node, node.Config().RPC.ListenAddress } -// DefaultTestingNodeConfig constructs the default in-memory node configuration for testing. -func DefaultTestingNodeConfig(t TestingTS, gnoroot string) *gnoland.InMemoryNodeConfig { +// TestingNodeConfig constructs an in-memory node configuration +// with default packages and genesis transactions already loaded. +// It will return the default creator address of the loaded packages. +func TestingNodeConfig(t TestingTS, gnoroot string) (*gnoland.InMemoryNodeConfig, bft.Address) { + cfg := TestingMinimalNodeConfig(t, gnoroot) + + creator := crypto.MustAddressFromString(DefaultAccount_Address) // test1 + + balances := LoadDefaultGenesisBalanceFile(t, gnoroot) + txs := []std.Tx{} + txs = append(txs, LoadDefaultPackages(t, creator, gnoroot)...) + txs = append(txs, LoadDefaultGenesisTXsFile(t, cfg.Genesis.ChainID, gnoroot)...) + + cfg.Genesis.AppState = gnoland.GnoGenesisState{ + Balances: balances, + Txs: txs, + } + + return cfg, creator +} + +// TestingMinimalNodeConfig constructs the default minimal in-memory node configuration for testing. +func TestingMinimalNodeConfig(t TestingTS, gnoroot string) *gnoland.InMemoryNodeConfig { tmconfig := DefaultTestingTMConfig(gnoroot) // Create Mocked Identity @@ -59,11 +80,6 @@ func DefaultTestingNodeConfig(t TestingTS, gnoroot string) *gnoland.InMemoryNode } func DefaultTestingGenesisConfig(t TestingTS, gnoroot string, self crypto.PubKey, tmconfig *tmcfg.Config) *bft.GenesisDoc { - pkgCreator := crypto.MustAddressFromString(DefaultAccount_Address) // test1 - - // Load genesis packages - genesisPackagesTxs := LoadDefaultPackages(t, pkgCreator, gnoroot) - return &bft.GenesisDoc{ GenesisTime: time.Now(), ChainID: tmconfig.ChainID(), @@ -84,13 +100,8 @@ func DefaultTestingGenesisConfig(t TestingTS, gnoroot string, self crypto.PubKey }, }, AppState: gnoland.GnoGenesisState{ - Balances: []gnoland.Balance{ - { - Address: pkgCreator, - Value: std.MustParseCoins("10000000000000ugnot"), - }, - }, - Txs: genesisPackagesTxs, + Balances: []gnoland.Balance{}, + Txs: []std.Tx{}, }, } } From f6d4b247b5fdebe219bb79b18621ea7caeea700e Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Fri, 27 Oct 2023 15:56:07 -0400 Subject: [PATCH 27/38] fix: creator balances Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/pkg/integration/testing_node.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/gno.land/pkg/integration/testing_node.go b/gno.land/pkg/integration/testing_node.go index 93fb16f683c..8c3f44c9308 100644 --- a/gno.land/pkg/integration/testing_node.go +++ b/gno.land/pkg/integration/testing_node.go @@ -100,10 +100,16 @@ func DefaultTestingGenesisConfig(t TestingTS, gnoroot string, self crypto.PubKey }, }, AppState: gnoland.GnoGenesisState{ - Balances: []gnoland.Balance{}, - Txs: []std.Tx{}, + Balances: []gnoland.Balance{ + { + Address: crypto.MustAddressFromString(DefaultAccount_Address), + Value: std.MustParseCoins("10000000000000ugnot"), + }, + }, + Txs: []std.Tx{}, }, } + } // LoadDefaultPackages loads the default packages for testing using a given creator address and gnoroot directory. From fbe030018fa4a78a1a9b3d99eae76a23d1a02285 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Fri, 27 Oct 2023 15:58:50 -0400 Subject: [PATCH 28/38] fix: lint Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/pkg/integration/testing_node.go | 1 - 1 file changed, 1 deletion(-) diff --git a/gno.land/pkg/integration/testing_node.go b/gno.land/pkg/integration/testing_node.go index 8c3f44c9308..4461a0af27a 100644 --- a/gno.land/pkg/integration/testing_node.go +++ b/gno.land/pkg/integration/testing_node.go @@ -109,7 +109,6 @@ func DefaultTestingGenesisConfig(t TestingTS, gnoroot string, self crypto.PubKey Txs: []std.Tx{}, }, } - } // LoadDefaultPackages loads the default packages for testing using a given creator address and gnoroot directory. From f747694983a4067125432eb1cc17e6a3edcf28a7 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Fri, 27 Oct 2023 16:46:41 -0400 Subject: [PATCH 29/38] chore: lint Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/cmd/gnoland/start.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gno.land/cmd/gnoland/start.go b/gno.land/cmd/gnoland/start.go index 64164c828ac..618f4f87a09 100644 --- a/gno.land/cmd/gnoland/start.go +++ b/gno.land/cmd/gnoland/start.go @@ -61,7 +61,7 @@ func newStartCmd(io *commands.IO) *commands.Command { func (c *startCfg) RegisterFlags(fs *flag.FlagSet) { gnoroot := gnoland.MustGuessGnoRootDir() defaultGenesisBalancesFile := filepath.Join(gnoroot, "gno.land", "genesis", "genesis_balances.txt") - defaultGenesisTxsFile := filepath.Join(gnoroot, "gno.land/genesis/genesis_txs.txt") + defaultGenesisTxsFile := filepath.Join(gnoroot, "gno.land", "genesis", "genesis_txs.txt") fs.BoolVar( &c.skipFailingGenesisTxs, From 248b08389db0974f48f3a7a7435381515903da50 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Fri, 27 Oct 2023 19:59:35 -0400 Subject: [PATCH 30/38] fix: remove panic & fix potential nil error Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/cmd/gnoweb/main.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/gno.land/cmd/gnoweb/main.go b/gno.land/cmd/gnoweb/main.go index 83d9664bb8e..b080e0b403d 100644 --- a/gno.land/cmd/gnoweb/main.go +++ b/gno.land/cmd/gnoweb/main.go @@ -486,12 +486,11 @@ func writeError(w http.ResponseWriter, err error) { // XXX: writeError should return an error page template. w.WriteHeader(500) - panic(err) - details := errors.Unwrap(err).Error() - main := err.Error() + fmt.Println("main", err.Error()) - fmt.Println("main", main) - fmt.Println("details", details) + if details := errors.Unwrap(err); details != nil { + fmt.Println("details", details.Error()) + } w.Write([]byte(err.Error())) } From 92ce8a81408c3591ceb1c787d74462ddc94f70a6 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Mon, 30 Oct 2023 15:43:31 +0100 Subject: [PATCH 31/38] chore: lint Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/pkg/gnoland/app.go | 13 +++++++------ gno.land/pkg/gnoland/types.go | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index 757c5f2248c..89fa597cd77 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -207,7 +207,7 @@ var ( ) func MustGuessGnoRootDir() string { - root, err := guessGnoRootDir() + root, err := GuessGnoRootDir() if err != nil { panic(err) } @@ -217,6 +217,12 @@ func MustGuessGnoRootDir() string { func GuessGnoRootDir() (string, error) { var err error + + // First try to get the root directory from the GNOROOT environment variable. + if rootdir := os.Getenv("GNOROOT"); rootdir != "" { + return filepath.Clean(rootdir), nil + } + guessOnce.Do(func() { gnoroot, err = guessGnoRootDir() }) @@ -225,11 +231,6 @@ func GuessGnoRootDir() (string, error) { } func guessGnoRootDir() (string, error) { - // First try to get the root directory from the GNOROOT environment variable. - if rootdir := os.Getenv("GNOROOT"); rootdir != "" { - return filepath.Clean(rootdir), nil - } - // Try to guess GNOROOT using the nearest go.mod. if gobin, err := exec.LookPath("go"); err == nil { // If GNOROOT is not set, try to guess the root directory using the `go list` command. diff --git a/gno.land/pkg/gnoland/types.go b/gno.land/pkg/gnoland/types.go index 7dba82bc6a3..8e120136482 100644 --- a/gno.land/pkg/gnoland/types.go +++ b/gno.land/pkg/gnoland/types.go @@ -52,7 +52,7 @@ func (b *Balance) UnmarshalJSON(data []byte) error { return b.Parse(string(data)) } -func (b *Balance) Marshaljson() ([]byte, error) { +func (b *Balance) MarshalJSON() ([]byte, error) { return []byte(b.String()), nil } From 77b416b203a94bfecdc417f4aa696148628333d2 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Mon, 30 Oct 2023 19:24:01 +0100 Subject: [PATCH 32/38] fix: use gnoland.Balance in `genesis` command Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/cmd/genesis/balances_add.go | 131 ++++++------------- gno.land/cmd/genesis/balances_add_test.go | 107 +++++++-------- gno.land/cmd/genesis/balances_export_test.go | 31 +++-- gno.land/cmd/genesis/balances_remove.go | 2 +- gno.land/cmd/genesis/balances_remove_test.go | 15 +-- gno.land/cmd/genesis/types.go | 22 +--- gno.land/cmd/genesis/verify.go | 5 +- gno.land/cmd/genesis/verify_test.go | 8 +- gno.land/pkg/gnoland/types.go | 28 +++- 9 files changed, 143 insertions(+), 206 deletions(-) diff --git a/gno.land/cmd/genesis/balances_add.go b/gno.land/cmd/genesis/balances_add.go index 276e48690a8..8b569261dc0 100644 --- a/gno.land/cmd/genesis/balances_add.go +++ b/gno.land/cmd/genesis/balances_add.go @@ -8,32 +8,22 @@ import ( "fmt" "io" "os" - "regexp" - "strconv" "strings" "github.com/gnolang/gno/gno.land/pkg/gnoland" "github.com/gnolang/gno/tm2/pkg/amino" "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/commands" - "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/sdk/bank" "github.com/gnolang/gno/tm2/pkg/std" _ "github.com/gnolang/gno/gno.land/pkg/sdk/vm" ) -var ( - balanceRegex = regexp.MustCompile(`^(\w+)=(\d+)ugnot$`) - amountRegex = regexp.MustCompile(`^(\d+)ugnot$`) -) - var ( errNoBalanceSource = errors.New("at least one balance source must be set") errBalanceParsingAborted = errors.New("balance parsing aborted") - errInvalidBalanceFormat = errors.New("invalid balance format encountered") errInvalidAddress = errors.New("invalid address encountered") - errInvalidAmount = errors.New("invalid amount encountered") ) type balancesAddCfg struct { @@ -152,7 +142,7 @@ func execBalancesAdd(ctx context.Context, cfg *balancesAddCfg, io *commands.IO) // Construct the initial genesis balance sheet state := genesis.AppState.(gnoland.GnoGenesisState) - genesisBalances, err := extractGenesisBalances(state) + genesisBalances, err := mapGenesisBalancesFromState(state) if err != nil { return err } @@ -190,12 +180,11 @@ func getBalancesFromEntries(entries []string) (accountBalances, error) { balances := make(accountBalances) for _, entry := range entries { - accountBalance, err := getBalanceFromEntry(entry) - if err != nil { - return nil, fmt.Errorf("unable to extract balance data, %w", err) + var balance gnoland.Balance + if err := balance.Parse(entry); err != nil { + return nil, fmt.Errorf("unable to parse balance entry: %w", err) } - - balances[accountBalance.address] = accountBalance.amount + balances[balance.Address] = balance } return balances, nil @@ -220,12 +209,12 @@ func getBalancesFromSheet(sheet io.Reader) (accountBalances, error) { continue } - accountBalance, err := getBalanceFromEntry(entry) - if err != nil { + var balance gnoland.Balance + if err := balance.Parse(entry); err != nil { return nil, fmt.Errorf("unable to extract balance data, %w", err) } - balances[accountBalance.address] = accountBalance.amount + balances[balance.Address] = balance } if err := scanner.Err(); err != nil { @@ -262,21 +251,19 @@ func getBalancesFromTransactions( if err := amino.UnmarshalJSON(line, &tx); err != nil { io.ErrPrintfln( - "invalid amino JSON encountered: %s", + "invalid amino JSON encountered: %q", string(line), ) continue } - feeAmount, err := getAmountFromEntry(tx.Fee.GasFee.String()) - if err != nil { + feeAmount := std.NewCoins(tx.Fee.GasFee) + if feeAmount.AmountOf("ugnot") <= 0 { io.ErrPrintfln( - "invalid gas fee amount encountered: %s", + "invalid gas fee amount encountered: %q", tx.Fee.GasFee.String(), ) - - continue } for _, msg := range tx.Msgs { @@ -286,13 +273,12 @@ func getBalancesFromTransactions( msgSend := msg.(bank.MsgSend) - sendAmount, err := getAmountFromEntry(msgSend.Amount.String()) - if err != nil { + sendAmount := msgSend.Amount + if sendAmount.AmountOf("ugnot") <= 0 { io.ErrPrintfln( "invalid send amount encountered: %s", msgSend.Amount.String(), ) - continue } @@ -304,27 +290,35 @@ func getBalancesFromTransactions( // causes an accounts balance to go < 0. In these cases, // we initialize the account (it is present in the balance sheet), but // with the balance of 0 - from := balances[msgSend.FromAddress] - to := balances[msgSend.ToAddress] - to += sendAmount + from := balances[msgSend.FromAddress].Value + to := balances[msgSend.ToAddress].Value + + to = to.Add(sendAmount) - if from < sendAmount || from < feeAmount { + if from.IsAllLT(sendAmount) || from.IsAllLT(feeAmount) { // Account cannot cover send amount / fee // (see message above) - from = 0 + from = std.NewCoins(std.NewCoin("ugnot", 0)) } - if from > sendAmount { - from -= sendAmount + if from.IsAllGT(sendAmount) { + from = from.Sub(sendAmount) } - if from > feeAmount { - from -= feeAmount + if from.IsAllGT(feeAmount) { + from = from.Sub(feeAmount) } - balances[msgSend.FromAddress] = from - balances[msgSend.ToAddress] = to + // Set new balance + balances[msgSend.FromAddress] = gnoland.Balance{ + Address: msgSend.FromAddress, + Value: from, + } + balances[msgSend.ToAddress] = gnoland.Balance{ + Address: msgSend.ToAddress, + Value: to, + } } } } @@ -340,65 +334,14 @@ func getBalancesFromTransactions( return balances, nil } -// getAmountFromEntry -func getAmountFromEntry(entry string) (int64, error) { - matches := amountRegex.FindStringSubmatch(entry) - - // Check if there is a match - if len(matches) != 2 { - return 0, fmt.Errorf( - "invalid amount, %s", - entry, - ) - } - - amount, err := strconv.ParseInt(matches[1], 10, 64) - if err != nil { - return 0, fmt.Errorf("invalid amount, %s", matches[1]) - } - - return amount, nil -} - -// getBalanceFromEntry extracts the account balance information -// from a single line in the form of:
=ugnot -func getBalanceFromEntry(entry string) (*accountBalance, error) { - matches := balanceRegex.FindStringSubmatch(entry) - if len(matches) != 3 { - return nil, fmt.Errorf("%w, %s", errInvalidBalanceFormat, entry) - } - - // Validate the address - address, err := crypto.AddressFromString(matches[1]) - if err != nil { - return nil, fmt.Errorf("%w, %w", errInvalidAddress, err) - } - - // Validate the amount - amount, err := strconv.ParseInt(matches[2], 10, 64) - if err != nil { - return nil, fmt.Errorf("%w, %w", errInvalidAmount, err) - } - - return &accountBalance{ - address: address, - amount: amount, - }, nil -} - -// extractGenesisBalances extracts the initial account balances from the +// mapGenesisBalancesFromState extracts the initial account balances from the // genesis app state -func extractGenesisBalances(state gnoland.GnoGenesisState) (accountBalances, error) { +func mapGenesisBalancesFromState(state gnoland.GnoGenesisState) (accountBalances, error) { // Construct the initial genesis balance sheet genesisBalances := make(accountBalances) - for _, entry := range state.Balances { - accountBalance, err := getBalanceFromEntry(entry) - if err != nil { - return nil, fmt.Errorf("invalid genesis balance entry, %w", err) - } - - genesisBalances[accountBalance.address] = accountBalance.amount + for _, balance := range state.Balances { + genesisBalances[balance.Address] = balance } return genesisBalances, nil diff --git a/gno.land/cmd/genesis/balances_add_test.go b/gno.land/cmd/genesis/balances_add_test.go index f986ee85274..78ecbd5bfd7 100644 --- a/gno.land/cmd/genesis/balances_add_test.go +++ b/gno.land/cmd/genesis/balances_add_test.go @@ -98,7 +98,7 @@ func TestGenesis_Balances_Add(t *testing.T) { tempGenesis.Name(), } - amount := int64(10) + amount := std.NewCoins(std.NewCoin("ugnot", 10)) for _, dummyKey := range dummyKeys { args = append(args, "--single") @@ -107,7 +107,7 @@ func TestGenesis_Balances_Add(t *testing.T) { fmt.Sprintf( "%s=%dugnot", dummyKey.Address().String(), - amount, + amount.AmountOf("ugnot"), ), ) } @@ -127,16 +127,13 @@ func TestGenesis_Balances_Add(t *testing.T) { require.Equal(t, len(dummyKeys), len(state.Balances)) - for _, entry := range state.Balances { - accountBalance, err := getBalanceFromEntry(entry) - require.NoError(t, err) - + for _, balance := range state.Balances { // Find the appropriate key // (the genesis is saved with randomized balance order) found := false for _, dummyKey := range dummyKeys { - if dummyKey.Address().String() == accountBalance.address.String() { - assert.Equal(t, amount, accountBalance.amount) + if dummyKey.Address().String() == balance.Address.String() { + assert.Equal(t, amount, balance.Value) found = true break @@ -144,7 +141,7 @@ func TestGenesis_Balances_Add(t *testing.T) { } if !found { - t.Fatalf("unexpected entry with address %s found", accountBalance.address.String()) + t.Fatalf("unexpected entry with address %s found", balance.Address.String()) } } }) @@ -159,7 +156,7 @@ func TestGenesis_Balances_Add(t *testing.T) { require.NoError(t, genesis.SaveAs(tempGenesis.Name())) dummyKeys := getDummyKeys(t, 10) - amount := int64(10) + amount := std.NewCoins(std.NewCoin("ugnot", 10)) balances := make([]string, len(dummyKeys)) @@ -170,7 +167,7 @@ func TestGenesis_Balances_Add(t *testing.T) { balances[index] = fmt.Sprintf( "%s=%dugnot", key.Address().String(), - amount, + amount.AmountOf("ugnot"), ) } @@ -207,16 +204,13 @@ func TestGenesis_Balances_Add(t *testing.T) { require.Equal(t, len(dummyKeys), len(state.Balances)) - for _, entry := range state.Balances { - accountBalance, err := getBalanceFromEntry(entry) - require.NoError(t, err) - + for _, balance := range state.Balances { // Find the appropriate key // (the genesis is saved with randomized balance order) found := false for _, dummyKey := range dummyKeys { - if dummyKey.Address().String() == accountBalance.address.String() { - assert.Equal(t, amount, accountBalance.amount) + if dummyKey.Address().String() == balance.Address.String() { + assert.Equal(t, amount, balance.Value) found = true break @@ -224,7 +218,7 @@ func TestGenesis_Balances_Add(t *testing.T) { } if !found { - t.Fatalf("unexpected entry with address %s found", accountBalance.address.String()) + t.Fatalf("unexpected entry with address %s found", balance.Address.String()) } } }) @@ -240,8 +234,8 @@ func TestGenesis_Balances_Add(t *testing.T) { var ( dummyKeys = getDummyKeys(t, 10) - amount = int64(10) - amountCoins = std.NewCoins(std.NewCoin("ugnot", amount)) + amount = std.NewCoins(std.NewCoin("ugnot", 10)) + amountCoins = std.NewCoins(std.NewCoin("ugnot", 10)) gasFee = std.NewCoin("ugnot", 1000000) txs = make([]std.Tx, 0) ) @@ -309,10 +303,7 @@ func TestGenesis_Balances_Add(t *testing.T) { require.Equal(t, len(dummyKeys), len(state.Balances)) - for _, entry := range state.Balances { - accountBalance, err := getBalanceFromEntry(entry) - require.NoError(t, err) - + for _, balance := range state.Balances { // Find the appropriate key // (the genesis is saved with randomized balance order) found := false @@ -321,11 +312,11 @@ func TestGenesis_Balances_Add(t *testing.T) { if index == 0 { // the first address should // have a balance of 0 - checkAmount = 0 + checkAmount = std.NewCoins(std.NewCoin("ugnot", 0)) } - if dummyKey.Address().String() == accountBalance.address.String() { - assert.Equal(t, checkAmount, accountBalance.amount) + if dummyKey.Address().String() == balance.Address.String() { + assert.True(t, balance.Value.IsEqual(checkAmount)) found = true break @@ -333,7 +324,7 @@ func TestGenesis_Balances_Add(t *testing.T) { } if !found { - t.Fatalf("unexpected entry with address %s found", accountBalance.address.String()) + t.Fatalf("unexpected entry with address %s found", balance.Address.String()) } } }) @@ -349,12 +340,11 @@ func TestGenesis_Balances_Add(t *testing.T) { genesis := getDefaultGenesis() state := gnoland.GnoGenesisState{ // Set an initial balance value - Balances: []string{ - fmt.Sprintf( - "%s=%dugnot", - dummyKeys[0].Address().String(), - 100, - ), + Balances: []gnoland.Balance{ + { + Address: dummyKeys[0].Address(), + Value: std.NewCoins(std.NewCoin("ugnot", 100)), + }, }, } genesis.AppState = state @@ -369,7 +359,7 @@ func TestGenesis_Balances_Add(t *testing.T) { tempGenesis.Name(), } - amount := int64(10) + amount := std.NewCoins(std.NewCoin("ugnot", 10)) for _, dummyKey := range dummyKeys { args = append(args, "--single") @@ -378,7 +368,7 @@ func TestGenesis_Balances_Add(t *testing.T) { fmt.Sprintf( "%s=%dugnot", dummyKey.Address().String(), - amount, + amount.AmountOf("ugnot"), ), ) } @@ -398,16 +388,13 @@ func TestGenesis_Balances_Add(t *testing.T) { require.Equal(t, len(dummyKeys), len(state.Balances)) - for _, entry := range state.Balances { - accountBalance, err := getBalanceFromEntry(entry) - require.NoError(t, err) - + for _, balance := range state.Balances { // Find the appropriate key // (the genesis is saved with randomized balance order) found := false for _, dummyKey := range dummyKeys { - if dummyKey.Address().String() == accountBalance.address.String() { - assert.Equal(t, amount, accountBalance.amount) + if dummyKey.Address().String() == balance.Address.String() { + assert.Equal(t, amount, balance.Value) found = true break @@ -415,7 +402,7 @@ func TestGenesis_Balances_Add(t *testing.T) { } if !found { - t.Fatalf("unexpected entry with address %s found", accountBalance.address.String()) + t.Fatalf("unexpected entry with address %s found", balance.Address.String()) } } }) @@ -429,7 +416,7 @@ func TestBalances_GetBalancesFromEntries(t *testing.T) { // Generate dummy keys dummyKeys := getDummyKeys(t, 2) - amount := int64(10) + amount := std.NewCoins(std.NewCoin("ugnot", 10)) balances := make([]string, len(dummyKeys)) @@ -437,7 +424,7 @@ func TestBalances_GetBalancesFromEntries(t *testing.T) { balances[index] = fmt.Sprintf( "%s=%dugnot", key.Address().String(), - amount, + amount.AmountOf("ugnot"), ) } @@ -447,7 +434,7 @@ func TestBalances_GetBalancesFromEntries(t *testing.T) { // Validate the balance map assert.Len(t, balanceMap, len(dummyKeys)) for _, key := range dummyKeys { - assert.Equal(t, amount, balanceMap[key.Address()]) + assert.Equal(t, amount, balanceMap[key.Address()].Value) } }) @@ -461,7 +448,7 @@ func TestBalances_GetBalancesFromEntries(t *testing.T) { balanceMap, err := getBalancesFromEntries(balances) assert.Nil(t, balanceMap) - assert.ErrorContains(t, err, errInvalidBalanceFormat.Error()) + assert.ErrorContains(t, err, "malformed entry") }) t.Run("malformed balance, invalid address", func(t *testing.T) { @@ -474,7 +461,7 @@ func TestBalances_GetBalancesFromEntries(t *testing.T) { balanceMap, err := getBalancesFromEntries(balances) assert.Nil(t, balanceMap) - assert.ErrorContains(t, err, errInvalidAddress.Error()) + assert.ErrorContains(t, err, "invalid address") }) t.Run("malformed balance, invalid amount", func(t *testing.T) { @@ -493,7 +480,7 @@ func TestBalances_GetBalancesFromEntries(t *testing.T) { balanceMap, err := getBalancesFromEntries(balances) assert.Nil(t, balanceMap) - assert.ErrorContains(t, err, errInvalidAmount.Error()) + assert.ErrorContains(t, err, "invalid amount") }) } @@ -505,7 +492,7 @@ func TestBalances_GetBalancesFromSheet(t *testing.T) { // Generate dummy keys dummyKeys := getDummyKeys(t, 2) - amount := int64(10) + amount := std.NewCoins(std.NewCoin("ugnot", 10)) balances := make([]string, len(dummyKeys)) @@ -513,7 +500,7 @@ func TestBalances_GetBalancesFromSheet(t *testing.T) { balances[index] = fmt.Sprintf( "%s=%dugnot", key.Address().String(), - amount, + amount.AmountOf("ugnot"), ) } @@ -524,7 +511,7 @@ func TestBalances_GetBalancesFromSheet(t *testing.T) { // Validate the balance map assert.Len(t, balanceMap, len(dummyKeys)) for _, key := range dummyKeys { - assert.Equal(t, amount, balanceMap[key.Address()]) + assert.Equal(t, amount, balanceMap[key.Address()].Value) } }) @@ -546,7 +533,7 @@ func TestBalances_GetBalancesFromSheet(t *testing.T) { balanceMap, err := getBalancesFromSheet(reader) assert.Nil(t, balanceMap) - assert.ErrorContains(t, err, errInvalidAmount.Error()) + assert.ErrorContains(t, err, "invalid amount") }) } @@ -558,8 +545,8 @@ func TestBalances_GetBalancesFromTransactions(t *testing.T) { var ( dummyKeys = getDummyKeys(t, 10) - amount = int64(10) - amountCoins = std.NewCoins(std.NewCoin("ugnot", amount)) + amount = std.NewCoins(std.NewCoin("ugnot", 10)) + amountCoins = std.NewCoins(std.NewCoin("ugnot", 10)) gasFee = std.NewCoin("ugnot", 1000000) txs = make([]std.Tx, 0) ) @@ -605,10 +592,10 @@ func TestBalances_GetBalancesFromTransactions(t *testing.T) { // Validate the balance map assert.Len(t, balanceMap, len(dummyKeys)) for _, key := range dummyKeys[1:] { - assert.Equal(t, amount, balanceMap[key.Address()]) + assert.Equal(t, amount, balanceMap[key.Address()].Value) } - assert.Equal(t, int64(0), balanceMap[sender.Address()]) + assert.Equal(t, std.Coins{}, balanceMap[sender.Address()].Value) }) t.Run("malformed transaction, invalid fee amount", func(t *testing.T) { @@ -616,8 +603,7 @@ func TestBalances_GetBalancesFromTransactions(t *testing.T) { var ( dummyKeys = getDummyKeys(t, 10) - amount = int64(10) - amountCoins = std.NewCoins(std.NewCoin("ugnot", amount)) + amountCoins = std.NewCoins(std.NewCoin("ugnot", 10)) gasFee = std.NewCoin("gnos", 1) // invalid fee txs = make([]std.Tx, 0) ) @@ -669,8 +655,7 @@ func TestBalances_GetBalancesFromTransactions(t *testing.T) { var ( dummyKeys = getDummyKeys(t, 10) - amount = int64(10) - amountCoins = std.NewCoins(std.NewCoin("gnogno", amount)) // invalid send amount + amountCoins = std.NewCoins(std.NewCoin("gnogno", 10)) // invalid send amount gasFee = std.NewCoin("ugnot", 1) txs = make([]std.Tx, 0) ) diff --git a/gno.land/cmd/genesis/balances_export_test.go b/gno.land/cmd/genesis/balances_export_test.go index 33e4f7bc800..dd8a9038feb 100644 --- a/gno.land/cmd/genesis/balances_export_test.go +++ b/gno.land/cmd/genesis/balances_export_test.go @@ -3,31 +3,30 @@ package main import ( "bufio" "context" - "fmt" "testing" "github.com/gnolang/gno/gno.land/pkg/gnoland" "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/std" "github.com/gnolang/gno/tm2/pkg/testutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -// getDummyBalanceLines generates dummy balance lines -func getDummyBalanceLines(t *testing.T, count int) []string { +// getDummyBalances generates dummy balance lines +func getDummyBalances(t *testing.T, count int) []gnoland.Balance { t.Helper() dummyKeys := getDummyKeys(t, count) - amount := int64(10) + amount := std.NewCoins(std.NewCoin("ugnot", 10)) - balances := make([]string, len(dummyKeys)) + balances := make([]gnoland.Balance, len(dummyKeys)) for index, key := range dummyKeys { - balances[index] = fmt.Sprintf( - "%s=%dugnot", - key.Address().String(), - amount, - ) + balances[index] = gnoland.Balance{ + Address: key.Address(), + Value: amount, + } } return balances @@ -85,7 +84,7 @@ func TestGenesis_Balances_Export(t *testing.T) { genesis := getDefaultGenesis() genesis.AppState = gnoland.GnoGenesisState{ - Balances: getDummyBalanceLines(t, 1), + Balances: getDummyBalances(t, 1), } require.NoError(t, genesis.SaveAs(tempGenesis.Name())) @@ -107,7 +106,7 @@ func TestGenesis_Balances_Export(t *testing.T) { t.Parallel() // Generate dummy balances - balances := getDummyBalanceLines(t, 10) + balances := getDummyBalances(t, 10) tempGenesis, cleanup := testutils.NewTestFile(t) t.Cleanup(cleanup) @@ -139,9 +138,13 @@ func TestGenesis_Balances_Export(t *testing.T) { // Validate the transactions were written down scanner := bufio.NewScanner(outputFile) - outputBalances := make([]string, 0) + outputBalances := make([]gnoland.Balance, 0) for scanner.Scan() { - outputBalances = append(outputBalances, scanner.Text()) + var balance gnoland.Balance + err := balance.Parse(scanner.Text()) + require.NoError(t, err) + + outputBalances = append(outputBalances, balance) } require.NoError(t, scanner.Err()) diff --git a/gno.land/cmd/genesis/balances_remove.go b/gno.land/cmd/genesis/balances_remove.go index f7e9092dc3b..f4286d95ad2 100644 --- a/gno.land/cmd/genesis/balances_remove.go +++ b/gno.land/cmd/genesis/balances_remove.go @@ -71,7 +71,7 @@ func execBalancesRemove(cfg *balancesRemoveCfg, io *commands.IO) error { // Construct the initial genesis balance sheet state := genesis.AppState.(gnoland.GnoGenesisState) - genesisBalances, err := extractGenesisBalances(state) + genesisBalances, err := mapGenesisBalancesFromState(state) if err != nil { return err } diff --git a/gno.land/cmd/genesis/balances_remove_test.go b/gno.land/cmd/genesis/balances_remove_test.go index 29179c43604..b53131986ee 100644 --- a/gno.land/cmd/genesis/balances_remove_test.go +++ b/gno.land/cmd/genesis/balances_remove_test.go @@ -2,12 +2,12 @@ package main import ( "context" - "fmt" "testing" "github.com/gnolang/gno/gno.land/pkg/gnoland" "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/std" "github.com/gnolang/gno/tm2/pkg/testutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -70,12 +70,11 @@ func TestGenesis_Balances_Remove(t *testing.T) { genesis := getDefaultGenesis() state := gnoland.GnoGenesisState{ // Set an initial balance value - Balances: []string{ - fmt.Sprintf( - "%s=%dugnot", - dummyKey.Address().String(), - 100, - ), + Balances: []gnoland.Balance{ + { + Address: dummyKey.Address(), + Value: std.NewCoins(std.NewCoin("ugnot", 100)), + }, }, } genesis.AppState = state @@ -118,7 +117,7 @@ func TestGenesis_Balances_Remove(t *testing.T) { genesis := getDefaultGenesis() state := gnoland.GnoGenesisState{ - Balances: []string{}, // Empty initial balance + Balances: []gnoland.Balance{}, // Empty initial balance } genesis.AppState = state require.NoError(t, genesis.SaveAs(tempGenesis.Name())) diff --git a/gno.land/cmd/genesis/types.go b/gno.land/cmd/genesis/types.go index 208eaddb6da..dba39ea8ec1 100644 --- a/gno.land/cmd/genesis/types.go +++ b/gno.land/cmd/genesis/types.go @@ -1,8 +1,7 @@ package main import ( - "fmt" - + "github.com/gnolang/gno/gno.land/pkg/gnoland" "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/std" ) @@ -39,23 +38,14 @@ func (i *txStore) leftMerge(b txStore) error { return nil } -type ( - accountBalances map[types.Address]int64 // address -> balance (ugnot) - accountBalance struct { - address types.Address - amount int64 - } -) +type accountBalances map[types.Address]gnoland.Balance // address -> balance (ugnot) // toList linearizes the account balances map -func (a accountBalances) toList() []string { - balances := make([]string, 0, len(a)) +func (a accountBalances) toList() []gnoland.Balance { + balances := make([]gnoland.Balance, 0, len(a)) - for address, balance := range a { - balances = append( - balances, - fmt.Sprintf("%s=%dugnot", address, balance), - ) + for _, balance := range a { + balances = append(balances, balance) } return balances diff --git a/gno.land/cmd/genesis/verify.go b/gno.land/cmd/genesis/verify.go index ba51f5801f6..6c877ca51ec 100644 --- a/gno.land/cmd/genesis/verify.go +++ b/gno.land/cmd/genesis/verify.go @@ -9,7 +9,6 @@ import ( "github.com/gnolang/gno/gno.land/pkg/gnoland" "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/commands" - "github.com/gnolang/gno/tm2/pkg/std" ) var errInvalidGenesisState = errors.New("invalid genesis state type") @@ -68,8 +67,8 @@ func execVerify(cfg *verifyCfg, io *commands.IO) error { // Validate the initial balances for _, balance := range state.Balances { - if _, parseErr := std.ParseCoins(balance); parseErr != nil { - return fmt.Errorf("invalid balance %s, %w", balance, parseErr) + if err := balance.Verify(); err != nil { + return fmt.Errorf("invalid balance: %w", err) } } } diff --git a/gno.land/cmd/genesis/verify_test.go b/gno.land/cmd/genesis/verify_test.go index fcc5305b9d0..8388949898b 100644 --- a/gno.land/cmd/genesis/verify_test.go +++ b/gno.land/cmd/genesis/verify_test.go @@ -44,7 +44,7 @@ func TestGenesis_Verify(t *testing.T) { g := getValidTestGenesis() g.AppState = gnoland.GnoGenesisState{ - Balances: []string{}, + Balances: []gnoland.Balance{}, Txs: []std.Tx{ {}, }, @@ -74,8 +74,8 @@ func TestGenesis_Verify(t *testing.T) { g := getValidTestGenesis() g.AppState = gnoland.GnoGenesisState{ - Balances: []string{ - "dummybalance", + Balances: []gnoland.Balance{ + {}, }, Txs: []std.Tx{}, } @@ -103,7 +103,7 @@ func TestGenesis_Verify(t *testing.T) { g := getValidTestGenesis() g.AppState = gnoland.GnoGenesisState{ - Balances: []string{}, + Balances: []gnoland.Balance{}, Txs: []std.Tx{}, } diff --git a/gno.land/pkg/gnoland/types.go b/gno.land/pkg/gnoland/types.go index 8e120136482..4a1a68babe1 100644 --- a/gno.land/pkg/gnoland/types.go +++ b/gno.land/pkg/gnoland/types.go @@ -1,6 +1,7 @@ package gnoland import ( + "errors" "fmt" "strings" @@ -9,6 +10,11 @@ import ( "github.com/gnolang/gno/tm2/pkg/std" ) +var ( + ErrBalanceEmptyAddress = errors.New("balance address is empty") + ErrBalanceEmptyAmount = errors.New("balance amount is empty") +) + type GnoAccount struct { std.BaseAccount } @@ -27,22 +33,34 @@ type Balance struct { Value std.Coins } -func (b *Balance) Parse(line string) error { - parts := strings.Split(strings.TrimSpace(line), "=") //
= +func (b *Balance) Verify() error { + if b.Address.IsZero() { + return ErrBalanceEmptyAddress + } + + if b.Value.Len() == 0 { + return ErrBalanceEmptyAmount + } + + return nil +} + +func (b *Balance) Parse(entry string) error { + parts := strings.Split(strings.TrimSpace(entry), "=") //
= if len(parts) != 2 { - return fmt.Errorf("invalid balance line: %q", line) + return fmt.Errorf("malformed entry: %q", entry) } var err error b.Address, err = crypto.AddressFromBech32(parts[0]) if err != nil { - return fmt.Errorf("invalid balance addr %s: %w", parts[0], err) + return fmt.Errorf("invalid address %q: %w", parts[0], err) } b.Value, err = std.ParseCoins(parts[1]) if err != nil { - return fmt.Errorf("invalid balance coins %s: %w", parts[1], err) + return fmt.Errorf("invalid amount %q: %w", parts[1], err) } return nil From cc3fe3e0cd69f1ac9b56ab0cfefef5b7fa679712 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Mon, 6 Nov 2023 22:32:33 +0100 Subject: [PATCH 33/38] fix: use amino marshaler Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/pkg/gnoland/types.go | 8 +-- gno.land/pkg/gnoland/types_test.go | 99 ++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 gno.land/pkg/gnoland/types_test.go diff --git a/gno.land/pkg/gnoland/types.go b/gno.land/pkg/gnoland/types.go index 4a1a68babe1..8d55fc122eb 100644 --- a/gno.land/pkg/gnoland/types.go +++ b/gno.land/pkg/gnoland/types.go @@ -66,12 +66,12 @@ func (b *Balance) Parse(entry string) error { return nil } -func (b *Balance) UnmarshalJSON(data []byte) error { - return b.Parse(string(data)) +func (b *Balance) UnmarshalAmino(rep string) error { + return b.Parse(rep) } -func (b *Balance) MarshalJSON() ([]byte, error) { - return []byte(b.String()), nil +func (b Balance) MarshalAmino() (string, error) { + return b.String(), nil } func (b Balance) String() string { diff --git a/gno.land/pkg/gnoland/types_test.go b/gno.land/pkg/gnoland/types_test.go new file mode 100644 index 00000000000..f70ccaa62a4 --- /dev/null +++ b/gno.land/pkg/gnoland/types_test.go @@ -0,0 +1,99 @@ +package gnoland + +import ( + "fmt" + "testing" + + "github.com/gnolang/gno/tm2/pkg/amino" + bft "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/std" + "github.com/jaekwon/testify/assert" + "github.com/jaekwon/testify/require" +) + +func TestBalance_Verify(t *testing.T) { + validAddress := crypto.MustAddressFromString("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") + emptyCoins := std.Coins{} + nonEmptyCoins := std.NewCoins(std.NewCoin("test", 100)) + + tests := []struct { + name string + balance Balance + expectErr bool + }{ + {"empty coins", Balance{Address: validAddress, Value: emptyCoins}, true}, + {"empty address", Balance{Address: bft.Address{}, Value: nonEmptyCoins}, true}, + {"valid balance", Balance{Address: validAddress, Value: nonEmptyCoins}, false}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + err := tc.balance.Verify() + if tc.expectErr { + assert.Error(t, err, fmt.Sprintf("TestVerifyBalance: %s", tc.name)) + } else { + assert.NoError(t, err, fmt.Sprintf("TestVerifyBalance: %s", tc.name)) + } + }) + } +} + +func TestBalance_Parse(t *testing.T) { + validAddress := crypto.MustAddressFromString("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") + validBalance := Balance{Address: validAddress, Value: std.NewCoins(std.NewCoin("test", 100))} + + tests := []struct { + name string + entry string + expected Balance + expectErr bool + }{ + {"valid entry", "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5=100test", validBalance, false}, + {"invalid address", "invalid=100test", Balance{}, true}, + {"empty amount", "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5=", Balance{}, true}, + {"incomplete entry", "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", Balance{}, true}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + balance := Balance{} + err := balance.Parse(tc.entry) + if tc.expectErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tc.expected, balance) + } + }) + } +} + +func TestBalance_AminoUnmarshalJSON(t *testing.T) { + expected := Balance{ + Address: crypto.MustAddressFromString("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5"), + Value: std.MustParseCoins("100ugnot"), + } + value := fmt.Sprintf("[%q]", expected.String()) + + var balances []Balance + err := amino.UnmarshalJSON([]byte(value), &balances) + require.NoError(t, err) + require.Len(t, balances, 1, "there should be one balance after unmarshaling") + + balance := balances[0] + require.Equal(t, expected.Address, balance.Address) + require.True(t, expected.Value.IsEqual(balance.Value)) +} + +func TestBalance_AminoMarshalJSON(t *testing.T) { + expected := Balance{ + Address: crypto.MustAddressFromString("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5"), + Value: std.MustParseCoins("100ugnot"), + } + expectedJSON := fmt.Sprintf("[%q]", expected.String()) + + balancesJSON, err := amino.MarshalJSON([]Balance{expected}) + require.NoError(t, err) + require.JSONEq(t, expectedJSON, string(balancesJSON)) +} From 41ef47cfd805f20903ce3428af50c5a39c83ea5c Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Mon, 6 Nov 2023 22:34:14 +0100 Subject: [PATCH 34/38] fix: amount can be empty Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/pkg/gnoland/types_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/gno.land/pkg/gnoland/types_test.go b/gno.land/pkg/gnoland/types_test.go index f70ccaa62a4..63dd94075f7 100644 --- a/gno.land/pkg/gnoland/types_test.go +++ b/gno.land/pkg/gnoland/types_test.go @@ -51,7 +51,6 @@ func TestBalance_Parse(t *testing.T) { }{ {"valid entry", "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5=100test", validBalance, false}, {"invalid address", "invalid=100test", Balance{}, true}, - {"empty amount", "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5=", Balance{}, true}, {"incomplete entry", "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", Balance{}, true}, } From d80231c952d283dd83842132d51c535d1bc55bee Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Mon, 6 Nov 2023 22:36:14 +0100 Subject: [PATCH 35/38] fix: rename Balance.Value into Balance.Amount Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/cmd/genesis/balances_add.go | 8 ++++---- gno.land/cmd/genesis/balances_add_test.go | 18 +++++++++--------- gno.land/cmd/genesis/balances_export_test.go | 2 +- gno.land/cmd/genesis/balances_remove_test.go | 2 +- gno.land/pkg/gnoland/app.go | 2 +- gno.land/pkg/gnoland/genesis.go | 2 +- gno.land/pkg/gnoland/types.go | 8 ++++---- gno.land/pkg/gnoland/types_test.go | 14 +++++++------- gno.land/pkg/integration/testing_node.go | 2 +- 9 files changed, 29 insertions(+), 29 deletions(-) diff --git a/gno.land/cmd/genesis/balances_add.go b/gno.land/cmd/genesis/balances_add.go index 8b569261dc0..8df193c770c 100644 --- a/gno.land/cmd/genesis/balances_add.go +++ b/gno.land/cmd/genesis/balances_add.go @@ -291,8 +291,8 @@ func getBalancesFromTransactions( // we initialize the account (it is present in the balance sheet), but // with the balance of 0 - from := balances[msgSend.FromAddress].Value - to := balances[msgSend.ToAddress].Value + from := balances[msgSend.FromAddress].Amount + to := balances[msgSend.ToAddress].Amount to = to.Add(sendAmount) @@ -313,11 +313,11 @@ func getBalancesFromTransactions( // Set new balance balances[msgSend.FromAddress] = gnoland.Balance{ Address: msgSend.FromAddress, - Value: from, + Amount: from, } balances[msgSend.ToAddress] = gnoland.Balance{ Address: msgSend.ToAddress, - Value: to, + Amount: to, } } } diff --git a/gno.land/cmd/genesis/balances_add_test.go b/gno.land/cmd/genesis/balances_add_test.go index 78ecbd5bfd7..bd66f0f446b 100644 --- a/gno.land/cmd/genesis/balances_add_test.go +++ b/gno.land/cmd/genesis/balances_add_test.go @@ -133,7 +133,7 @@ func TestGenesis_Balances_Add(t *testing.T) { found := false for _, dummyKey := range dummyKeys { if dummyKey.Address().String() == balance.Address.String() { - assert.Equal(t, amount, balance.Value) + assert.Equal(t, amount, balance.Amount) found = true break @@ -210,7 +210,7 @@ func TestGenesis_Balances_Add(t *testing.T) { found := false for _, dummyKey := range dummyKeys { if dummyKey.Address().String() == balance.Address.String() { - assert.Equal(t, amount, balance.Value) + assert.Equal(t, amount, balance.Amount) found = true break @@ -316,7 +316,7 @@ func TestGenesis_Balances_Add(t *testing.T) { } if dummyKey.Address().String() == balance.Address.String() { - assert.True(t, balance.Value.IsEqual(checkAmount)) + assert.True(t, balance.Amount.IsEqual(checkAmount)) found = true break @@ -343,7 +343,7 @@ func TestGenesis_Balances_Add(t *testing.T) { Balances: []gnoland.Balance{ { Address: dummyKeys[0].Address(), - Value: std.NewCoins(std.NewCoin("ugnot", 100)), + Amount: std.NewCoins(std.NewCoin("ugnot", 100)), }, }, } @@ -394,7 +394,7 @@ func TestGenesis_Balances_Add(t *testing.T) { found := false for _, dummyKey := range dummyKeys { if dummyKey.Address().String() == balance.Address.String() { - assert.Equal(t, amount, balance.Value) + assert.Equal(t, amount, balance.Amount) found = true break @@ -434,7 +434,7 @@ func TestBalances_GetBalancesFromEntries(t *testing.T) { // Validate the balance map assert.Len(t, balanceMap, len(dummyKeys)) for _, key := range dummyKeys { - assert.Equal(t, amount, balanceMap[key.Address()].Value) + assert.Equal(t, amount, balanceMap[key.Address()].Amount) } }) @@ -511,7 +511,7 @@ func TestBalances_GetBalancesFromSheet(t *testing.T) { // Validate the balance map assert.Len(t, balanceMap, len(dummyKeys)) for _, key := range dummyKeys { - assert.Equal(t, amount, balanceMap[key.Address()].Value) + assert.Equal(t, amount, balanceMap[key.Address()].Amount) } }) @@ -592,10 +592,10 @@ func TestBalances_GetBalancesFromTransactions(t *testing.T) { // Validate the balance map assert.Len(t, balanceMap, len(dummyKeys)) for _, key := range dummyKeys[1:] { - assert.Equal(t, amount, balanceMap[key.Address()].Value) + assert.Equal(t, amount, balanceMap[key.Address()].Amount) } - assert.Equal(t, std.Coins{}, balanceMap[sender.Address()].Value) + assert.Equal(t, std.Coins{}, balanceMap[sender.Address()].Amount) }) t.Run("malformed transaction, invalid fee amount", func(t *testing.T) { diff --git a/gno.land/cmd/genesis/balances_export_test.go b/gno.land/cmd/genesis/balances_export_test.go index dd8a9038feb..a5314339c86 100644 --- a/gno.land/cmd/genesis/balances_export_test.go +++ b/gno.land/cmd/genesis/balances_export_test.go @@ -25,7 +25,7 @@ func getDummyBalances(t *testing.T, count int) []gnoland.Balance { for index, key := range dummyKeys { balances[index] = gnoland.Balance{ Address: key.Address(), - Value: amount, + Amount: amount, } } diff --git a/gno.land/cmd/genesis/balances_remove_test.go b/gno.land/cmd/genesis/balances_remove_test.go index b53131986ee..633399538b8 100644 --- a/gno.land/cmd/genesis/balances_remove_test.go +++ b/gno.land/cmd/genesis/balances_remove_test.go @@ -73,7 +73,7 @@ func TestGenesis_Balances_Remove(t *testing.T) { Balances: []gnoland.Balance{ { Address: dummyKey.Address(), - Value: std.NewCoins(std.NewCoin("ugnot", 100)), + Amount: std.NewCoins(std.NewCoin("ugnot", 100)), }, }, } diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index 89fa597cd77..cc2f15ce1cc 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -149,7 +149,7 @@ func InitChainer(baseApp *sdk.BaseApp, acctKpr auth.AccountKeeperI, bankKpr bank for _, bal := range genState.Balances { acc := acctKpr.NewAccountWithAddress(ctx, bal.Address) acctKpr.SetAccount(ctx, acc) - err := bankKpr.SetCoins(ctx, bal.Address, bal.Value) + err := bankKpr.SetCoins(ctx, bal.Address, bal.Amount) if err != nil { panic(err) } diff --git a/gno.land/pkg/gnoland/genesis.go b/gno.land/pkg/gnoland/genesis.go index 94c9d6cbf03..ec098fffaf7 100644 --- a/gno.land/pkg/gnoland/genesis.go +++ b/gno.land/pkg/gnoland/genesis.go @@ -51,7 +51,7 @@ func LoadGenesisBalancesFile(path string) ([]Balance, error) { balances = append(balances, Balance{ Address: addr, - Value: coins, + Amount: coins, }) } diff --git a/gno.land/pkg/gnoland/types.go b/gno.land/pkg/gnoland/types.go index 8d55fc122eb..5d68064c9c5 100644 --- a/gno.land/pkg/gnoland/types.go +++ b/gno.land/pkg/gnoland/types.go @@ -30,7 +30,7 @@ type GnoGenesisState struct { type Balance struct { Address bft.Address - Value std.Coins + Amount std.Coins } func (b *Balance) Verify() error { @@ -38,7 +38,7 @@ func (b *Balance) Verify() error { return ErrBalanceEmptyAddress } - if b.Value.Len() == 0 { + if b.Amount.Len() == 0 { return ErrBalanceEmptyAmount } @@ -58,7 +58,7 @@ func (b *Balance) Parse(entry string) error { return fmt.Errorf("invalid address %q: %w", parts[0], err) } - b.Value, err = std.ParseCoins(parts[1]) + b.Amount, err = std.ParseCoins(parts[1]) if err != nil { return fmt.Errorf("invalid amount %q: %w", parts[1], err) } @@ -75,5 +75,5 @@ func (b Balance) MarshalAmino() (string, error) { } func (b Balance) String() string { - return fmt.Sprintf("%s=%s", b.Address.String(), b.Value.String()) + return fmt.Sprintf("%s=%s", b.Address.String(), b.Amount.String()) } diff --git a/gno.land/pkg/gnoland/types_test.go b/gno.land/pkg/gnoland/types_test.go index 63dd94075f7..1124d1a07a4 100644 --- a/gno.land/pkg/gnoland/types_test.go +++ b/gno.land/pkg/gnoland/types_test.go @@ -22,9 +22,9 @@ func TestBalance_Verify(t *testing.T) { balance Balance expectErr bool }{ - {"empty coins", Balance{Address: validAddress, Value: emptyCoins}, true}, - {"empty address", Balance{Address: bft.Address{}, Value: nonEmptyCoins}, true}, - {"valid balance", Balance{Address: validAddress, Value: nonEmptyCoins}, false}, + {"empty coins", Balance{Address: validAddress, Amount: emptyCoins}, true}, + {"empty address", Balance{Address: bft.Address{}, Amount: nonEmptyCoins}, true}, + {"valid balance", Balance{Address: validAddress, Amount: nonEmptyCoins}, false}, } for _, tc := range tests { @@ -41,7 +41,7 @@ func TestBalance_Verify(t *testing.T) { func TestBalance_Parse(t *testing.T) { validAddress := crypto.MustAddressFromString("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") - validBalance := Balance{Address: validAddress, Value: std.NewCoins(std.NewCoin("test", 100))} + validBalance := Balance{Address: validAddress, Amount: std.NewCoins(std.NewCoin("test", 100))} tests := []struct { name string @@ -71,7 +71,7 @@ func TestBalance_Parse(t *testing.T) { func TestBalance_AminoUnmarshalJSON(t *testing.T) { expected := Balance{ Address: crypto.MustAddressFromString("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5"), - Value: std.MustParseCoins("100ugnot"), + Amount: std.MustParseCoins("100ugnot"), } value := fmt.Sprintf("[%q]", expected.String()) @@ -82,13 +82,13 @@ func TestBalance_AminoUnmarshalJSON(t *testing.T) { balance := balances[0] require.Equal(t, expected.Address, balance.Address) - require.True(t, expected.Value.IsEqual(balance.Value)) + require.True(t, expected.Amount.IsEqual(balance.Amount)) } func TestBalance_AminoMarshalJSON(t *testing.T) { expected := Balance{ Address: crypto.MustAddressFromString("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5"), - Value: std.MustParseCoins("100ugnot"), + Amount: std.MustParseCoins("100ugnot"), } expectedJSON := fmt.Sprintf("[%q]", expected.String()) diff --git a/gno.land/pkg/integration/testing_node.go b/gno.land/pkg/integration/testing_node.go index 4461a0af27a..1ca7e11eb63 100644 --- a/gno.land/pkg/integration/testing_node.go +++ b/gno.land/pkg/integration/testing_node.go @@ -103,7 +103,7 @@ func DefaultTestingGenesisConfig(t TestingTS, gnoroot string, self crypto.PubKey Balances: []gnoland.Balance{ { Address: crypto.MustAddressFromString(DefaultAccount_Address), - Value: std.MustParseCoins("10000000000000ugnot"), + Amount: std.MustParseCoins("10000000000000ugnot"), }, }, Txs: []std.Tx{}, From 5b23e9f5fc52338c1df5581103d381329b2dc404 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Mon, 6 Nov 2023 22:38:05 +0100 Subject: [PATCH 36/38] chore: rename Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/pkg/gnoland/types_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gno.land/pkg/gnoland/types_test.go b/gno.land/pkg/gnoland/types_test.go index 1124d1a07a4..97222d0cdfd 100644 --- a/gno.land/pkg/gnoland/types_test.go +++ b/gno.land/pkg/gnoland/types_test.go @@ -14,17 +14,17 @@ import ( func TestBalance_Verify(t *testing.T) { validAddress := crypto.MustAddressFromString("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") - emptyCoins := std.Coins{} - nonEmptyCoins := std.NewCoins(std.NewCoin("test", 100)) + emptyAmount := std.Coins{} + nonEmptyAmount := std.NewCoins(std.NewCoin("test", 100)) tests := []struct { name string balance Balance expectErr bool }{ - {"empty coins", Balance{Address: validAddress, Amount: emptyCoins}, true}, - {"empty address", Balance{Address: bft.Address{}, Amount: nonEmptyCoins}, true}, - {"valid balance", Balance{Address: validAddress, Amount: nonEmptyCoins}, false}, + {"empty amount", Balance{Address: validAddress, Amount: emptyAmount}, true}, + {"empty address", Balance{Address: bft.Address{}, Amount: nonEmptyAmount}, true}, + {"valid balance", Balance{Address: validAddress, Amount: nonEmptyAmount}, false}, } for _, tc := range tests { From 6292f7bbc59a08db6221e392c335232afa81a004 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Mon, 6 Nov 2023 22:40:35 +0100 Subject: [PATCH 37/38] chore: remove gnoroot singleton Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/pkg/gnoland/app.go | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index cc2f15ce1cc..b5322325d0d 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -8,7 +8,6 @@ import ( "path/filepath" "runtime" "strings" - "sync" "github.com/gnolang/gno/gno.land/pkg/sdk/vm" "github.com/gnolang/gno/tm2/pkg/amino" @@ -201,11 +200,6 @@ func EndBlocker(vmk vm.VMKeeperI) func(ctx sdk.Context, req abci.RequestEndBlock // XXX: all the method bellow should be removed in favor of // https://github.com/gnolang/gno/pull/1233 -var ( - guessOnce sync.Once - gnoroot string -) - func MustGuessGnoRootDir() string { root, err := GuessGnoRootDir() if err != nil { @@ -223,11 +217,7 @@ func GuessGnoRootDir() (string, error) { return filepath.Clean(rootdir), nil } - guessOnce.Do(func() { - gnoroot, err = guessGnoRootDir() - }) - - return gnoroot, err + return guessGnoRootDir() } func guessGnoRootDir() (string, error) { From 844995a8540dfbbd9380ad5e8cc270bd36de15c0 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Tue, 7 Nov 2023 09:23:52 +0100 Subject: [PATCH 38/38] chore: lint Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/cmd/genesis/balances_add_test.go | 2 +- gno.land/cmd/genesis/balances_export_test.go | 2 +- gno.land/cmd/genesis/balances_remove_test.go | 2 +- gno.land/pkg/gnoland/app.go | 6 ------ gno.land/pkg/gnoland/genesis.go | 2 +- 5 files changed, 4 insertions(+), 10 deletions(-) diff --git a/gno.land/cmd/genesis/balances_add_test.go b/gno.land/cmd/genesis/balances_add_test.go index bd66f0f446b..73e2fe148a2 100644 --- a/gno.land/cmd/genesis/balances_add_test.go +++ b/gno.land/cmd/genesis/balances_add_test.go @@ -343,7 +343,7 @@ func TestGenesis_Balances_Add(t *testing.T) { Balances: []gnoland.Balance{ { Address: dummyKeys[0].Address(), - Amount: std.NewCoins(std.NewCoin("ugnot", 100)), + Amount: std.NewCoins(std.NewCoin("ugnot", 100)), }, }, } diff --git a/gno.land/cmd/genesis/balances_export_test.go b/gno.land/cmd/genesis/balances_export_test.go index a5314339c86..d7441fd438f 100644 --- a/gno.land/cmd/genesis/balances_export_test.go +++ b/gno.land/cmd/genesis/balances_export_test.go @@ -25,7 +25,7 @@ func getDummyBalances(t *testing.T, count int) []gnoland.Balance { for index, key := range dummyKeys { balances[index] = gnoland.Balance{ Address: key.Address(), - Amount: amount, + Amount: amount, } } diff --git a/gno.land/cmd/genesis/balances_remove_test.go b/gno.land/cmd/genesis/balances_remove_test.go index 633399538b8..b9d10d0db08 100644 --- a/gno.land/cmd/genesis/balances_remove_test.go +++ b/gno.land/cmd/genesis/balances_remove_test.go @@ -73,7 +73,7 @@ func TestGenesis_Balances_Remove(t *testing.T) { Balances: []gnoland.Balance{ { Address: dummyKey.Address(), - Amount: std.NewCoins(std.NewCoin("ugnot", 100)), + Amount: std.NewCoins(std.NewCoin("ugnot", 100)), }, }, } diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index b5322325d0d..a8a2736c8d1 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -210,17 +210,11 @@ func MustGuessGnoRootDir() string { } func GuessGnoRootDir() (string, error) { - var err error - // First try to get the root directory from the GNOROOT environment variable. if rootdir := os.Getenv("GNOROOT"); rootdir != "" { return filepath.Clean(rootdir), nil } - return guessGnoRootDir() -} - -func guessGnoRootDir() (string, error) { // Try to guess GNOROOT using the nearest go.mod. if gobin, err := exec.LookPath("go"); err == nil { // If GNOROOT is not set, try to guess the root directory using the `go list` command. diff --git a/gno.land/pkg/gnoland/genesis.go b/gno.land/pkg/gnoland/genesis.go index ec098fffaf7..e809103469d 100644 --- a/gno.land/pkg/gnoland/genesis.go +++ b/gno.land/pkg/gnoland/genesis.go @@ -51,7 +51,7 @@ func LoadGenesisBalancesFile(path string) ([]Balance, error) { balances = append(balances, Balance{ Address: addr, - Amount: coins, + Amount: coins, }) }