diff --git a/gno.land/cmd/gnokey/main.go b/gno.land/cmd/gnokey/main.go index 1f3414dc893..28cb665eac1 100644 --- a/gno.land/cmd/gnokey/main.go +++ b/gno.land/cmd/gnokey/main.go @@ -5,11 +5,12 @@ import ( "fmt" "os" + "github.com/gnolang/gno/tm2/pkg/commands" "github.com/gnolang/gno/tm2/pkg/crypto/keys/client" ) func main() { - cmd := client.NewRootCmd() + cmd := client.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/gnoland/integration.go b/gno.land/cmd/gnoland/integration.go new file mode 100644 index 00000000000..06ab7d0f9a3 --- /dev/null +++ b/gno.land/cmd/gnoland/integration.go @@ -0,0 +1 @@ +package main diff --git a/gno.land/cmd/gnoland/integration_test.go b/gno.land/cmd/gnoland/integration_test.go new file mode 100644 index 00000000000..bba3b8bdab1 --- /dev/null +++ b/gno.land/cmd/gnoland/integration_test.go @@ -0,0 +1,365 @@ +package main + +import ( + "context" + "flag" + "fmt" + "hash/crc32" + "os/exec" + "path/filepath" + "strconv" + "strings" + "sync" + "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" + 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/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/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/jaekwon/testify/require" + "github.com/rogpeppe/go-internal/testscript" +) + +// XXX: 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" +) + +func TestTestdata(t *testing.T) { + testscript.Run(t, setupGnolandTestScript2(t, "testdata")) +} + +func setupGnolandTestScript2(t *testing.T, txtarDir string) testscript.Params { + t.Helper() + + goModPath, err := exec.Command("go", "env", "GOMOD").CombinedOutput() + require.NoError(t, err) + + gnoRootDir := filepath.Dir(string(goModPath)) + gnoHomeDir := filepath.Join(t.TempDir(), "gno") + gnoDataDir := filepath.Join(t.TempDir(), "data") + + var muNodes sync.Mutex + nodes := map[string]*node.Node{} + t.Cleanup(func() { + for id, n := range nodes { + if err := n.Stop(); err != nil { + panic(fmt.Errorf("node %q was unable to stop: %w", id, err)) + } + } + }) + + return testscript.Params{ + Dir: txtarDir, + Setup: func(env *testscript.Env) error { + kb, err := keys.NewKeyBaseFromDir(gnoHomeDir) + if err != nil { + return err + } + + kb.CreateAccount("test1", test1Seed, "", "", 0, 0) + env.Setenv("USER_SEED_test1", test1Seed) + env.Setenv("USER_ADDR_test1", test1Addr) + + env.Setenv("GNOROOT", gnoRootDir) + env.Setenv("GNOHOME", gnoHomeDir) + + return nil + }, + Cmds: map[string]func(ts *testscript.TestScript, neg bool, args []string){ + "gnoland": func(ts *testscript.TestScript, neg bool, args []string) { + muNodes.Lock() + defer muNodes.Unlock() + + if len(args) == 0 { + tsValidateError(ts, "gnoland", neg, fmt.Errorf("use gnoland [start|stop] command")) + return + } + + sid := getSessionID(ts) + + var cmd string + cmd, args = args[0], args[1:] + + var err error + switch cmd { + case "start": + if _, ok := nodes[sid]; ok { + err = fmt.Errorf("node %q already started", sid) + break + } + + dataDir := filepath.Join(gnoDataDir, sid) + var node *node.Node + if node, err = execTestingGnoland(t, dataDir, gnoRootDir, args); err == nil { + // XXX need mutex ? + nodes[sid] = node + + // get listen addr environement + // should have been updated with the right port on start + laddr := node.Config().RPC.ListenAddress + + // add default environement + ts.Setenv("RPC_ADDR", laddr) + ts.Setenv("GNODATA", gnoDataDir) + + // XXX: Use something similar to `require.Eventually` to check for node + // availability. For now, if this sleep duration is too short, the + // subsequent command might fail with an [internal error]. + time.Sleep(time.Millisecond * 300) + } + case "stop": + n, ok := nodes[sid] + if !ok { + err = fmt.Errorf("node %q not started cannot be stop", sid) + break + } + + if err = n.Stop(); err != nil { + delete(nodes, sid) + + // unset env dirs + ts.Setenv("RPC_ADDR", "") + ts.Setenv("GNODATA", "") + } + } + + tsValidateError(ts, "gnoland "+cmd, neg, err) + }, + "gnokey": func(ts *testscript.TestScript, neg bool, args []string) { + muNodes.Lock() + defer muNodes.Unlock() + + sid := getSessionID(ts) + + // Setup io command + io := commands.NewTestIO() + io.SetOut(commands.WriteNopCloser(ts.Stdout())) + io.SetErr(commands.WriteNopCloser(ts.Stderr())) + cmd := client.NewRootCmd(io) + + io.SetIn(strings.NewReader("\n")) // inject empty password to stdin + defaultArgs := []string{ + "-home", gnoHomeDir, + "-insecure-password-stdin=true", // there no use to not have this param by default + } + + if n, ok := nodes[sid]; ok { + if raddr := n.Config().RPC.ListenAddress; raddr != "" { + defaultArgs = append(defaultArgs, "-remote", raddr) + } + } + + // inject default argument, if duplicate + // arguments, it should be override by the ones + // user provided + args = append(defaultArgs, args...) + + err := cmd.ParseAndRun(context.Background(), args) + tsValidateError(ts, "gnokey", neg, err) + }, + }, + } +} + +func execTestingGnoland(t *testing.T, gnoDataDir, gnoRootDir string, args []string) (*node.Node, error) { + t.Helper() + + // Setup logger + logger := log.NewNopLogger() + + // Setup start config + scfg := &startCfg{} + { + fs := flag.NewFlagSet("start", flag.ExitOnError) + scfg.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, scfg, gnoRootDir); err != nil { + return nil, err + } + + // Create application and node + return createAppAndNode(cfg, logger, gnoRootDir, scfg) +} + +func getSessionID(ts *testscript.TestScript) string { + works := ts.Getenv("WORK") + sum := crc32.ChecksumIEEE([]byte(works)) + return strconv.FormatUint(uint64(sum), 16) +} + +func setupTestingGenesis(gnoDataDir string, cfg *config.Config, scfg *startCfg, gnoRootDir string) error { + newPrivValKey := cfg.PrivValidatorKeyFile() + newPrivValState := cfg.PrivValidatorStateFile() + priv := privval.LoadOrGenFilePV(newPrivValKey, newPrivValState) + + genesisFilePath := filepath.Join(gnoDataDir, cfg.Genesis) + osm.EnsureDir(filepath.Dir(genesisFilePath), 0o700) + if !osm.FileExists(genesisFilePath) { + genesisTxs := loadGenesisTxs(scfg.genesisTxsFile, scfg.chainID, scfg.genesisRemote) + pvPub := priv.GetPubKey() + + gen := &bft.GenesisDoc{ + GenesisTime: time.Now(), + ChainID: scfg.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(scfg.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, scfg *startCfg) (*node.Node, error) { + gnoApp, err := gnoland.NewCustomApp(gnoland.CustomAppConfig{ + Logger: logger, + GnoRootDir: gnoRootDir, + SkipFailingGenesisTxs: scfg.skipFailingGenesisTxs, + MaxCycles: scfg.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 { + ts.Logf("%s error: %v\n", cmd, err) + if !neg { + ts.Fatalf("unexpected %s command failure", cmd) + } + } else { + if neg { + ts.Fatalf("unexpected %s command success", cmd) + } + } +} diff --git a/gno.land/cmd/gnoland/testdata/addpkg.txtar b/gno.land/cmd/gnoland/testdata/addpkg.txtar new file mode 100644 index 00000000000..5e871b058ac --- /dev/null +++ b/gno.land/cmd/gnoland/testdata/addpkg.txtar @@ -0,0 +1,26 @@ +# test for add package + +## start a new node +gnoland start + +## add bar.gno package located in $WORK directory as gno.land/r/foobar/bar +gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/foobar/bar -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 + +## execute Render +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 + +-- bar.gno -- +package bar + +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/cmd/gnoland/testdata/basic_commands.txtar b/gno.land/cmd/gnoland/testdata/basic_commands.txtar new file mode 100644 index 00000000000..ee97d417d80 --- /dev/null +++ b/gno.land/cmd/gnoland/testdata/basic_commands.txtar @@ -0,0 +1,22 @@ +# test basic gnoland integrations commands + +## no arguments should fail +! gnoland + +## should be able to start +gnoland start + +## should not be able to start a node twice +! gnoland start + +## test1 account should be available on default +gnokey query auth/accounts/${USER_ADDR_test1} + +## invalid gnokey command should raise an error +! gnokey query foo/bar + +## should be able to stop default +gnoland stop + +## should not be able to stop a node twice +! gnoland stop diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index b10f251b115..dad87eaf44d 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -2,6 +2,8 @@ package gnoland import ( "fmt" + "os" + "os/exec" "path/filepath" "strings" @@ -20,12 +22,44 @@ import ( "github.com/gnolang/gno/tm2/pkg/store/iavl" ) +type CustomAppConfig struct { + DB dbm.DB + GnoRootDir string + SkipFailingGenesisTxs bool + Logger log.Logger + MaxCycles int64 +} + +func (c *CustomAppConfig) ApplyDefault() { + if c.Logger == nil { + c.Logger = log.NewNopLogger() + } + + if c.DB == nil { + c.DB = dbm.NewMemDB() + } + + if c.GnoRootDir == "" { + c.GnoRootDir = guessGnoRootDir() + } +} + +func (c *CustomAppConfig) validate() error { + if c.Logger == nil { + return fmt.Errorf("no logger provided") + } + + if c.DB == nil { + return fmt.Errorf("no db provided") + } + + return nil +} + // NewApp creates the GnoLand application. -func NewApp(rootDir string, skipFailingGenesisTxs bool, logger log.Logger, maxCycles int64) (abci.Application, error) { - // Get main DB. - db, err := dbm.NewDB("gnolang", dbm.GoLevelDBBackend, filepath.Join(rootDir, "data")) - if err != nil { - return nil, fmt.Errorf("error initializing database %q using path %q: %w", dbm.GoLevelDBBackend, rootDir, err) +func NewCustomApp(cfg CustomAppConfig) (abci.Application, error) { + if err := cfg.validate(); err != nil { + return nil, err } // Capabilities keys. @@ -33,21 +67,21 @@ func NewApp(rootDir string, skipFailingGenesisTxs bool, logger log.Logger, maxCy baseKey := store.NewStoreKey("base") // Create BaseApp. - baseApp := sdk.NewBaseApp("gnoland", logger, db, baseKey, mainKey) + baseApp := sdk.NewBaseApp("gnoland", cfg.Logger, cfg.DB, baseKey, mainKey) baseApp.SetAppVersion("dev") // Set mounts for BaseApp's MultiStore. - baseApp.MountStoreWithDB(mainKey, iavl.StoreConstructor, db) - baseApp.MountStoreWithDB(baseKey, dbadapter.StoreConstructor, db) + baseApp.MountStoreWithDB(mainKey, iavl.StoreConstructor, cfg.DB) + baseApp.MountStoreWithDB(baseKey, dbadapter.StoreConstructor, cfg.DB) // Construct keepers. acctKpr := auth.NewAccountKeeper(mainKey, ProtoGnoAccount) bankKpr := bank.NewBankKeeper(acctKpr) - stdlibsDir := filepath.Join("..", "gnovm", "stdlibs") - vmKpr := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, stdlibsDir, maxCycles) + stdlibsDir := filepath.Join(cfg.GnoRootDir, "gnovm", "stdlibs") + vmKpr := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, stdlibsDir, cfg.MaxCycles) // Set InitChainer - baseApp.SetInitChainer(InitChainer(baseApp, acctKpr, bankKpr, skipFailingGenesisTxs)) + baseApp.SetInitChainer(InitChainer(baseApp, acctKpr, bankKpr, cfg.SkipFailingGenesisTxs)) // Set AnteHandler authOptions := auth.AnteOptions{ @@ -88,6 +122,24 @@ func NewApp(rootDir string, skipFailingGenesisTxs bool, logger log.Logger, maxCy return baseApp, nil } +// NewApp creates the GnoLand application. +func NewApp(dataRootDir string, skipFailingGenesisTxs bool, logger log.Logger, maxCycles int64) (abci.Application, error) { + var err error + + var cfg CustomAppConfig + cfg.ApplyDefault() + + // Get main DB. + cfg.DB, err = dbm.NewDB("gnolang", dbm.GoLevelDBBackend, filepath.Join(dataRootDir, "data")) + if err != nil { + return nil, fmt.Errorf("error initializing database %q using path %q: %w", dbm.GoLevelDBBackend, dataRootDir, err) + } + + cfg.Logger = logger + + return NewCustomApp(cfg) +} + // InitChainer returns a function that can initialize the chain with genesis. func InitChainer(baseApp *sdk.BaseApp, acctKpr auth.AccountKeeperI, bankKpr bank.BankKeeperI, skipFailingGenesisTxs bool) func(sdk.Context, abci.RequestInitChain) abci.ResponseInitChain { return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { @@ -107,14 +159,15 @@ func InitChainer(baseApp *sdk.BaseApp, acctKpr auth.AccountKeeperI, bankKpr bank for i, tx := range genState.Txs { res := baseApp.Deliver(tx) if res.IsErr() { - fmt.Println("ERROR LOG:", res.Log) - fmt.Println("#", i, string(amino.MustMarshalJSON(tx))) + ctx.Logger().Error("LOG", res.Log) + ctx.Logger().Error("#", i, string(amino.MustMarshalJSON(tx))) + // NOTE: comment out to ignore. if !skipFailingGenesisTxs { panic(res.Error) } } else { - fmt.Println("SUCCESS:", string(amino.MustMarshalJSON(tx))) + ctx.Logger().Info("SUCCESS:", string(amino.MustMarshalJSON(tx))) } } // Done! @@ -146,3 +199,25 @@ func EndBlocker(vmk vm.VMKeeperI) func(ctx sdk.Context, req abci.RequestEndBlock return abci.ResponseEndBlock{} } } + +func guessGnoRootDir() string { + var rootdir string + + // first try to get the root directory from the GNOROOT environment variable. + if rootdir = os.Getenv("GNOROOT"); rootdir != "" { + return filepath.Clean(rootdir) + } + + 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)) + } + + return strings.TrimSpace(string(out)) + } + + panic("no go binary available, unable to determine gno root-dir path") +} diff --git a/gno.land/pkg/sdk/vm/keeper.go b/gno.land/pkg/sdk/vm/keeper.go index 28531f0a773..6f695e98558 100644 --- a/gno.land/pkg/sdk/vm/keeper.go +++ b/gno.land/pkg/sdk/vm/keeper.go @@ -188,7 +188,8 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) error { }) defer m2.Release() m2.RunMemPackage(memPkg, true) - fmt.Println("CPUCYCLES addpkg", m2.Cycles) + + ctx.Logger().Info("CPUCYCLES", "addpkg", m2.Cycles) return nil } @@ -270,7 +271,7 @@ func (vm *VMKeeper) Call(ctx sdk.Context, msg MsgCall) (res string, err error) { m.Release() }() rtvs := m.Eval(xn) - fmt.Println("CPUCYCLES call", m.Cycles) + ctx.Logger().Info("CPUCYCLES call: ", m.Cycles) for i, rtv := range rtvs { res = res + rtv.String() if i < len(rtvs)-1 { diff --git a/misc/docker-integration/integration_test.go b/misc/docker-integration/integration_test.go index 6ae0ea1d36e..cf3f09a0401 100644 --- a/misc/docker-integration/integration_test.go +++ b/misc/docker-integration/integration_test.go @@ -149,6 +149,7 @@ func startGnoland(t *testing.T) { "docker", "run", "-d", "--name", gnolandContainerName, + "-e", "GNOROOT=/opt/gno/src", "-w", "/opt/gno/src/gno.land", "gno:integration", "gnoland", diff --git a/tm2/pkg/crypto/keys/client/add.go b/tm2/pkg/crypto/keys/client/add.go index 5f90a9f874e..30b612a9de2 100644 --- a/tm2/pkg/crypto/keys/client/add.go +++ b/tm2/pkg/crypto/keys/client/add.go @@ -29,7 +29,7 @@ type addCfg struct { index uint64 } -func newAddCmd(rootCfg *baseCfg) *commands.Command { +func newAddCmd(rootCfg *baseCfg, io *commands.IO) *commands.Command { cfg := &addCfg{ rootCfg: rootCfg, } @@ -42,7 +42,7 @@ func newAddCmd(rootCfg *baseCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execAdd(cfg, args, commands.NewDefaultIO()) + return execAdd(cfg, args, io) }, ) } diff --git a/tm2/pkg/crypto/keys/client/addpkg.go b/tm2/pkg/crypto/keys/client/addpkg.go index 885e1f123d7..3de9a6de546 100644 --- a/tm2/pkg/crypto/keys/client/addpkg.go +++ b/tm2/pkg/crypto/keys/client/addpkg.go @@ -24,7 +24,7 @@ type addPkgCfg struct { deposit string } -func newAddPkgCmd(rootCfg *makeTxCfg) *commands.Command { +func newAddPkgCmd(rootCfg *makeTxCfg, io *commands.IO) *commands.Command { cfg := &addPkgCfg{ rootCfg: rootCfg, } @@ -37,7 +37,7 @@ func newAddPkgCmd(rootCfg *makeTxCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execAddPkg(cfg, args, commands.NewDefaultIO()) + return execAddPkg(cfg, args, io) }, ) } diff --git a/tm2/pkg/crypto/keys/client/broadcast.go b/tm2/pkg/crypto/keys/client/broadcast.go index 039a9557c38..f1d448495a6 100644 --- a/tm2/pkg/crypto/keys/client/broadcast.go +++ b/tm2/pkg/crypto/keys/client/broadcast.go @@ -23,7 +23,7 @@ type broadcastCfg struct { tx *std.Tx } -func newBroadcastCmd(rootCfg *baseCfg) *commands.Command { +func newBroadcastCmd(rootCfg *baseCfg, io *commands.IO) *commands.Command { cfg := &broadcastCfg{ rootCfg: rootCfg, } diff --git a/tm2/pkg/crypto/keys/client/call.go b/tm2/pkg/crypto/keys/client/call.go index bcb7be3e550..29fe9739a36 100644 --- a/tm2/pkg/crypto/keys/client/call.go +++ b/tm2/pkg/crypto/keys/client/call.go @@ -22,7 +22,7 @@ type callCfg struct { args commands.StringArr } -func newCallCmd(rootCfg *makeTxCfg) *commands.Command { +func newCallCmd(rootCfg *makeTxCfg, io *commands.IO) *commands.Command { cfg := &callCfg{ rootCfg: rootCfg, } @@ -35,7 +35,7 @@ func newCallCmd(rootCfg *makeTxCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execCall(cfg, args, commands.NewDefaultIO()) + return execCall(cfg, args, io) }, ) } diff --git a/tm2/pkg/crypto/keys/client/delete.go b/tm2/pkg/crypto/keys/client/delete.go index 0f216d3467c..e22ac30988c 100644 --- a/tm2/pkg/crypto/keys/client/delete.go +++ b/tm2/pkg/crypto/keys/client/delete.go @@ -16,7 +16,7 @@ type deleteCfg struct { force bool } -func newDeleteCmd(rootCfg *baseCfg) *commands.Command { +func newDeleteCmd(rootCfg *baseCfg, io *commands.IO) *commands.Command { cfg := &deleteCfg{ rootCfg: rootCfg, } @@ -29,7 +29,7 @@ func newDeleteCmd(rootCfg *baseCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execDelete(cfg, args, commands.NewDefaultIO()) + return execDelete(cfg, args, io) }, ) } diff --git a/tm2/pkg/crypto/keys/client/export.go b/tm2/pkg/crypto/keys/client/export.go index eda04a5c92f..6eff8aa97b3 100644 --- a/tm2/pkg/crypto/keys/client/export.go +++ b/tm2/pkg/crypto/keys/client/export.go @@ -18,7 +18,7 @@ type exportCfg struct { unsafe bool } -func newExportCmd(rootCfg *baseCfg) *commands.Command { +func newExportCmd(rootCfg *baseCfg, io *commands.IO) *commands.Command { cfg := &exportCfg{ rootCfg: rootCfg, } @@ -31,7 +31,7 @@ func newExportCmd(rootCfg *baseCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execExport(cfg, commands.NewDefaultIO()) + return execExport(cfg, io) }, ) } diff --git a/tm2/pkg/crypto/keys/client/generate.go b/tm2/pkg/crypto/keys/client/generate.go index b721e6704ce..d209bd70bd3 100644 --- a/tm2/pkg/crypto/keys/client/generate.go +++ b/tm2/pkg/crypto/keys/client/generate.go @@ -16,7 +16,7 @@ type generateCfg struct { customEntropy bool } -func newGenerateCmd(rootCfg *baseCfg) *commands.Command { +func newGenerateCmd(rootCfg *baseCfg, io *commands.IO) *commands.Command { cfg := &generateCfg{ rootCfg: rootCfg, } @@ -29,7 +29,7 @@ func newGenerateCmd(rootCfg *baseCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execGenerate(cfg, args, commands.NewDefaultIO()) + return execGenerate(cfg, args, io) }, ) } diff --git a/tm2/pkg/crypto/keys/client/generate_test.go b/tm2/pkg/crypto/keys/client/generate_test.go index 516912046b6..fae85b664ac 100644 --- a/tm2/pkg/crypto/keys/client/generate_test.go +++ b/tm2/pkg/crypto/keys/client/generate_test.go @@ -46,7 +46,7 @@ func Test_execGenerateUser(t *testing.T) { err = execGenerate(cfg, []string{}, io) require.NoError(t, err) - // Now provide "good" entropy but no answer + // Now provide "io.good" entropy but no answer fakeEntropy = strings.Repeat(":)", 40) + "\n" // entropy + accept count io.SetIn(strings.NewReader(fakeEntropy)) err = execGenerate(cfg, []string{}, io) diff --git a/tm2/pkg/crypto/keys/client/import.go b/tm2/pkg/crypto/keys/client/import.go index 5e0eeecabb5..e1d8af55861 100644 --- a/tm2/pkg/crypto/keys/client/import.go +++ b/tm2/pkg/crypto/keys/client/import.go @@ -18,7 +18,7 @@ type importCfg struct { unsafe bool } -func newImportCmd(rootCfg *baseCfg) *commands.Command { +func newImportCmd(rootCfg *baseCfg, io *commands.IO) *commands.Command { cfg := &importCfg{ rootCfg: rootCfg, } @@ -31,7 +31,7 @@ func newImportCmd(rootCfg *baseCfg) *commands.Command { }, cfg, func(_ context.Context, _ []string) error { - return execImport(cfg, commands.NewDefaultIO()) + return execImport(cfg, io) }, ) } diff --git a/tm2/pkg/crypto/keys/client/list.go b/tm2/pkg/crypto/keys/client/list.go index 50be35cef43..cb86feb2395 100644 --- a/tm2/pkg/crypto/keys/client/list.go +++ b/tm2/pkg/crypto/keys/client/list.go @@ -8,7 +8,7 @@ import ( "github.com/gnolang/gno/tm2/pkg/crypto/keys" ) -func newListCmd(rootCfg *baseCfg) *commands.Command { +func newListCmd(rootCfg *baseCfg, io *commands.IO) *commands.Command { return commands.NewCommand( commands.Metadata{ Name: "list", @@ -17,7 +17,7 @@ func newListCmd(rootCfg *baseCfg) *commands.Command { }, nil, func(_ context.Context, args []string) error { - return execList(rootCfg, args, commands.NewDefaultIO()) + return execList(rootCfg, args, io) }, ) } diff --git a/tm2/pkg/crypto/keys/client/maketx.go b/tm2/pkg/crypto/keys/client/maketx.go index cbcc6def0de..36214a5a983 100644 --- a/tm2/pkg/crypto/keys/client/maketx.go +++ b/tm2/pkg/crypto/keys/client/maketx.go @@ -17,7 +17,7 @@ type makeTxCfg struct { chainID string } -func newMakeTxCmd(rootCfg *baseCfg) *commands.Command { +func newMakeTxCmd(rootCfg *baseCfg, io *commands.IO) *commands.Command { cfg := &makeTxCfg{ rootCfg: rootCfg, } @@ -33,9 +33,9 @@ func newMakeTxCmd(rootCfg *baseCfg) *commands.Command { ) cmd.AddSubCommands( - newAddPkgCmd(cfg), - newSendCmd(cfg), - newCallCmd(cfg), + newAddPkgCmd(cfg, io), + newSendCmd(cfg, io), + newCallCmd(cfg, io), ) return cmd diff --git a/tm2/pkg/crypto/keys/client/query.go b/tm2/pkg/crypto/keys/client/query.go index 58923f8787c..50c1f257213 100644 --- a/tm2/pkg/crypto/keys/client/query.go +++ b/tm2/pkg/crypto/keys/client/query.go @@ -3,7 +3,6 @@ package client import ( "context" "flag" - "fmt" "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" ctypes "github.com/gnolang/gno/tm2/pkg/bft/rpc/core/types" @@ -22,7 +21,7 @@ type queryCfg struct { path string } -func newQueryCmd(rootCfg *baseCfg) *commands.Command { +func newQueryCmd(rootCfg *baseCfg, io *commands.IO) *commands.Command { cfg := &queryCfg{ rootCfg: rootCfg, } @@ -35,7 +34,7 @@ func newQueryCmd(rootCfg *baseCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execQuery(cfg, args) + return execQuery(cfg, args, io) }, ) } @@ -63,7 +62,7 @@ func (c *queryCfg) RegisterFlags(fs *flag.FlagSet) { ) } -func execQuery(cfg *queryCfg, args []string) error { +func execQuery(cfg *queryCfg, args []string, io *commands.IO) error { if len(args) != 1 { return flag.ErrHelp } @@ -76,15 +75,16 @@ func execQuery(cfg *queryCfg, args []string) error { } if qres.Response.Error != nil { - fmt.Printf("Log: %s\n", - qres.Response.Log) + io.Printf("Log: %+v\n", + qres.Response) return qres.Response.Error } + resdata := qres.Response.Data // XXX in general, how do we know what to show? // proof := qres.Response.Proof height := qres.Response.Height - fmt.Printf("height: %d\ndata: %s\n", + io.Printf("height: %d\ndata: %s\n", height, string(resdata)) return nil diff --git a/tm2/pkg/crypto/keys/client/root.go b/tm2/pkg/crypto/keys/client/root.go index ad8983f2bb9..550dd408b77 100644 --- a/tm2/pkg/crypto/keys/client/root.go +++ b/tm2/pkg/crypto/keys/client/root.go @@ -18,7 +18,7 @@ type baseCfg struct { BaseOptions } -func NewRootCmd() *commands.Command { +func NewRootCmd(io *commands.IO) *commands.Command { cfg := &baseCfg{} cmd := commands.NewCommand( @@ -35,17 +35,17 @@ func NewRootCmd() *commands.Command { ) cmd.AddSubCommands( - newAddCmd(cfg), - newDeleteCmd(cfg), - newGenerateCmd(cfg), - newExportCmd(cfg), - newImportCmd(cfg), - newListCmd(cfg), - newSignCmd(cfg), - newVerifyCmd(cfg), - newQueryCmd(cfg), - newBroadcastCmd(cfg), - newMakeTxCmd(cfg), + newAddCmd(cfg, io), + newDeleteCmd(cfg, io), + newGenerateCmd(cfg, io), + newExportCmd(cfg, io), + newImportCmd(cfg, io), + newListCmd(cfg, io), + newSignCmd(cfg, io), + newVerifyCmd(cfg, io), + newQueryCmd(cfg, io), + newBroadcastCmd(cfg, io), + newMakeTxCmd(cfg, io), ) return cmd diff --git a/tm2/pkg/crypto/keys/client/send.go b/tm2/pkg/crypto/keys/client/send.go index 8f82778b1e3..6d19ffcb393 100644 --- a/tm2/pkg/crypto/keys/client/send.go +++ b/tm2/pkg/crypto/keys/client/send.go @@ -21,7 +21,7 @@ type sendCfg struct { to string } -func newSendCmd(rootCfg *makeTxCfg) *commands.Command { +func newSendCmd(rootCfg *makeTxCfg, io *commands.IO) *commands.Command { cfg := &sendCfg{ rootCfg: rootCfg, } @@ -34,7 +34,7 @@ func newSendCmd(rootCfg *makeTxCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execSend(cfg, args, commands.NewDefaultIO()) + return execSend(cfg, args, io) }, ) } diff --git a/tm2/pkg/crypto/keys/client/sign.go b/tm2/pkg/crypto/keys/client/sign.go index bfc39647141..761e0d7a563 100644 --- a/tm2/pkg/crypto/keys/client/sign.go +++ b/tm2/pkg/crypto/keys/client/sign.go @@ -28,7 +28,7 @@ type signCfg struct { pass string } -func newSignCmd(rootCfg *baseCfg) *commands.Command { +func newSignCmd(rootCfg *baseCfg, io *commands.IO) *commands.Command { cfg := &signCfg{ rootCfg: rootCfg, } @@ -41,7 +41,7 @@ func newSignCmd(rootCfg *baseCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execSign(cfg, args, commands.NewDefaultIO()) + return execSign(cfg, args, io) }, ) } diff --git a/tm2/pkg/crypto/keys/client/verify.go b/tm2/pkg/crypto/keys/client/verify.go index 3dcc5f35dee..bb486c1a8fa 100644 --- a/tm2/pkg/crypto/keys/client/verify.go +++ b/tm2/pkg/crypto/keys/client/verify.go @@ -16,7 +16,7 @@ type verifyCfg struct { docPath string } -func newVerifyCmd(rootCfg *baseCfg) *commands.Command { +func newVerifyCmd(rootCfg *baseCfg, io *commands.IO) *commands.Command { cfg := &verifyCfg{ rootCfg: rootCfg, } @@ -29,7 +29,7 @@ func newVerifyCmd(rootCfg *baseCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execVerify(cfg, args, commands.NewDefaultIO()) + return execVerify(cfg, args, io) }, ) }