Skip to content

Commit

Permalink
chore: add gnoland init
Browse files Browse the repository at this point in the history
Signed-off-by: Manfred Touron <94029+moul@users.noreply.github.com>
  • Loading branch information
moul committed Sep 6, 2023
1 parent 4bd1789 commit 119fbe4
Show file tree
Hide file tree
Showing 6 changed files with 207 additions and 193 deletions.
99 changes: 99 additions & 0 deletions gno.land/cmd/gnoland/cmd_init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package main

import (
"context"
"flag"
"fmt"
"path/filepath"
"time"

"github.com/gnolang/gno/gno.land/pkg/gnoland"
"github.com/gnolang/gno/tm2/pkg/bft/config"
"github.com/gnolang/gno/tm2/pkg/bft/privval"
"github.com/gnolang/gno/tm2/pkg/commands"
"github.com/gnolang/gno/tm2/pkg/log"
osm "github.com/gnolang/gno/tm2/pkg/os"
)

func newInitCmd(io *commands.IO) *commands.Command {
cfg := &initCfg{}

return commands.NewCommand(
commands.Metadata{
Name: "init",
ShortUsage: "init [flags]",
ShortHelp: "Initialize a gnoland configuration on file-system",
},
cfg,
func(_ context.Context, args []string) error {
return execInit(cfg, args, io)
},
)
}

type initCfg struct {
// common
rootDir string
config string

// gnoland init specific
chainID string
genesisTxsFile string
genesisRemote string
skipFailingGenesisTxs bool
genesisBalancesFile string
genesisMaxVMCycles int64
}

func (c *initCfg) RegisterFlags(fs *flag.FlagSet) {
// common
fs.StringVar(&c.rootDir, "root-dir", "testdir", "directory for config and data")
fs.StringVar(&c.config, "config", "", "config file (optional)")

// init specific
fs.BoolVar(&c.skipFailingGenesisTxs, "skip-failing-genesis-txs", false, "don't panic when replaying invalid genesis txs")
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.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 execInit(c *initCfg, args []string, io *commands.IO) error {
logger := log.NewTMLogger(log.NewSyncWriter(io.Out))
rootDir := c.rootDir

cfg := config.LoadOrMakeConfigWithOptions(rootDir, 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)
if !osm.FileExists(genesisFilePath) {
genDoc := makeGenesisDoc(
priv.GetPubKey(),
c.chainID,
c.genesisBalancesFile,
loadGenesisTxs(c.genesisTxsFile, c.chainID, c.genesisRemote),
)
writeGenesisFile(genDoc, genesisFilePath)
}

// create application and node.
gnoApp, err := gnoland.NewApp(rootDir, c.skipFailingGenesisTxs, logger, c.genesisMaxVMCycles)
if err != nil {
return fmt.Errorf("error in creating new app: %w", err)
}

cfg.LocalApp = gnoApp

fmt.Fprintln(io.Err, "Node created.")
return nil
}
79 changes: 79 additions & 0 deletions gno.land/cmd/gnoland/cmd_start.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package main

import (
"context"
"flag"
"fmt"

"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/log"
osm "github.com/gnolang/gno/tm2/pkg/os"
)

func newStartCmd(io *commands.IO) *commands.Command {
cfg := &startCfg{}

return commands.NewCommand(
commands.Metadata{
Name: "start",
ShortUsage: "start [flags]",
ShortHelp: "Run the full node",
},
cfg,
func(_ context.Context, args []string) error {
return execStart(cfg, args, io)
},
)
}

type startCfg struct {
init initCfg
}

func (s startCfg)RegisterFlags(fs *flag.FlagSet) {

Check failure on line 36 in gno.land/cmd/gnoland/cmd_start.go

View workflow job for this annotation

GitHub Actions / lint

File is not `gofmt`-ed with `-s` (gofmt)
s.init.RegisterFlags(fs)
}

func execStart(c *startCfg, args []string, io *commands.IO) error {
rootDir := c.init.rootDir
logger := log.NewTMLogger(log.NewSyncWriter(io.Out))

// lazy init
if !config.Exists(rootDir) {
err := execInit(&c.init, []string{}, io) // XXX: create an helper instead of calling the cmd?
if err != nil {
return fmt.Errorf("lazy init: %w", err)
}
}

// load (existing) config
cfg := config.LoadOrMakeDefaultConfig(rootDir)
gnoApp, err := gnoland.NewApp(rootDir, c.init.skipFailingGenesisTxs, logger, c.init.genesisMaxVMCycles)
if err != nil {
return fmt.Errorf("error in creating new app: %w", err)
}
cfg.LocalApp = gnoApp

// create node
gnoNode, err := node.DefaultNewNode(cfg, logger)
if err != nil {
return fmt.Errorf("error in creating node: %w", err)
}

// start node
fmt.Fprintln(io.Err, "Starting Node...")
if err := gnoNode.Start(); err != nil {
return fmt.Errorf("error in start node: %w", err)
}

// run forever
osm.TrapSignal(func() {
if gnoNode.IsRunning() {
_ = gnoNode.Stop()
}
})
select {} // run forever
}
4 changes: 3 additions & 1 deletion gno.land/cmd/gnoland/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"context"

Check failure on line 5 in gno.land/cmd/gnoland/root.go

View workflow job for this annotation

GitHub Actions / lint

File is not `gofumpt`-ed (gofumpt)
"fmt"
"os"

Expand All @@ -24,7 +25,7 @@ func newRootCmd(io *commands.IO) *commands.Command {
cmd := commands.NewCommand(
commands.Metadata{
ShortUsage: "<subcommand> [flags] [<arg>...]",
ShortHelp: "Starts the gnoland blockchain node",
ShortHelp: "Manages the gnoland blockchain node",
Options: []ff.Option{
ff.WithConfigFileFlag("config"),
ff.WithConfigFileParser(fftoml.Parser),
Expand All @@ -35,6 +36,7 @@ func newRootCmd(io *commands.IO) *commands.Command {
)

cmd.AddSubCommands(
newInitCmd(io),
newStartCmd(io),
)

Expand Down
49 changes: 19 additions & 30 deletions gno.land/cmd/gnoland/start_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,38 +12,27 @@ import (
"github.com/stretchr/testify/require"
)

func TestStartInitialize(t *testing.T) {
cases := []struct {
args []string
}{
{[]string{"start", "--skip-start", "--skip-failing-genesis-txs"}},
// {[]string{"--skip-start"}},
// FIXME: test seems flappy as soon as we have multiple cases.
}
func TestInit(t *testing.T) {
os.Chdir(filepath.Join("..", "..")) // go to repo's root dir

Check failure on line 17 in gno.land/cmd/gnoland/start_test.go

View workflow job for this annotation

GitHub Actions / lint

File is not `gofumpt`-ed (gofumpt)
for _, tc := range cases {
name := strings.Join(tc.args, " ")
t.Run(name, func(t *testing.T) {
mockOut := bytes.NewBufferString("")
mockErr := bytes.NewBufferString("")
io := commands.NewTestIO()
io.SetOut(commands.WriteNopCloser(mockOut))
io.SetErr(commands.WriteNopCloser(mockErr))
cmd := newRootCmd(io)

t.Logf(`Running "gnoland %s"`, strings.Join(tc.args, " "))
err := cmd.ParseAndRun(context.Background(), tc.args)
require.NoError(t, err)

stdout := mockOut.String()
stderr := mockErr.String()

require.Contains(t, stderr, "Node created.", "failed to create node")
require.Contains(t, stderr, "'--skip-start' is set. Exiting.", "not exited with skip-start")
require.NotContains(t, stdout, "panic:")
})
}

Check failure on line 18 in gno.land/cmd/gnoland/start_test.go

View workflow job for this annotation

GitHub Actions / lint

File is not `gofmt`-ed with `-s` (gofmt)
mockOut := bytes.NewBufferString("")
mockErr := bytes.NewBufferString("")
io := commands.NewTestIO()
io.SetOut(commands.WriteNopCloser(mockOut))
io.SetErr(commands.WriteNopCloser(mockErr))
cmd := newRootCmd(io)

args := []string{"init", "--skip-failing-genesis-txs"}
t.Logf(`Running "gnoland %s"`, strings.Join(args, " "))
err := cmd.ParseAndRun(context.Background(), args)
require.NoError(t, err)

stdout := mockOut.String()
stderr := mockErr.String()

require.Contains(t, stderr, "Node created.")
require.NotContains(t, stdout, "panic:")
}

// TODO: test various configuration files?
Loading

0 comments on commit 119fbe4

Please sign in to comment.