From 5ad12cd2b668607f291ffa4787f9184e59970ca9 Mon Sep 17 00:00:00 2001 From: Guilhem Fanton <8671905+gfanton@users.noreply.github.com> Date: Mon, 15 Jan 2024 19:31:14 +0100 Subject: [PATCH] feat: remove `gnovm` and `gno.land` dependencies from tm2 (#1483) This PR shares the same goal as #1438: to make `tm2` completely unaware of `gnovm` and `gno.land`. However, it adopts a different strategy to achieve this, following https://github.com/gnolang/gno/pull/1438#issuecomment-1866686822. It will: - Exposed almost everything from the `tm2/crypto/keys/client` package: - `NewCommandXXX`, along with their specific structure configurations and fields, are now exposed to allow the creation of composable CLI tools. - `XXXHandler` (including Sign, Verify, Broadcast, etc.) are now exposed to enable external packages to reuse some logic from the `keys/client` package. - Moved specific `gnovm`/`gno.land` commands to the `gnokey` package: `run`, `call`, and `addpkg`. - In an effort to avoid duplicate code, an `ExecSignAndBroadcast` method has been exposed. However, it appears too specific, and I am considering duplicating it regardless. I would appreciate thoughts on this.
Contributors' checklist... - [ ] Added new tests, or not needed, or not feasible - [ ] Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory - [ ] Updated the official documentation or not needed - [ ] No breaking changes were made, or a `BREAKING CHANGE: xxx` message was included in the description - [ ] Added references to related issues and PRs - [ ] Provided any useful hints for running manual tests - [ ] Added new benchmarks to [generated graphs](https://gnoland.github.io/benchmarks), if any. More info [here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md).
--------- Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnokeykc/go.mod | 5 - contribs/gnokeykc/go.sum | 10 - gno.land/cmd/gnokey/main.go | 3 +- .../pkg/integration/testing_integration.go | 3 +- gno.land/pkg/keyscli/README.md | 12 + gno.land/pkg/keyscli/addpkg.go | 138 +++++++++++ .../client => gno.land/pkg/keyscli}/call.go | 63 ++--- gno.land/pkg/keyscli/maketx.go | 83 +++++++ gno.land/pkg/keyscli/root.go | 47 ++++ .../client => gno.land/pkg/keyscli}/run.go | 39 +-- tm2/pkg/crypto/keys/client/add.go | 86 +++---- tm2/pkg/crypto/keys/client/add_test.go | 16 +- tm2/pkg/crypto/keys/client/addpkg.go | 222 ------------------ tm2/pkg/crypto/keys/client/broadcast.go | 30 +-- tm2/pkg/crypto/keys/client/delete.go | 30 +-- tm2/pkg/crypto/keys/client/delete_test.go | 10 +- tm2/pkg/crypto/keys/client/export.go | 47 ++-- tm2/pkg/crypto/keys/client/export_test.go | 10 +- tm2/pkg/crypto/keys/client/generate.go | 20 +- tm2/pkg/crypto/keys/client/generate_test.go | 8 +- tm2/pkg/crypto/keys/client/import.go | 51 ++-- tm2/pkg/crypto/keys/client/import_test.go | 10 +- tm2/pkg/crypto/keys/client/list.go | 4 +- tm2/pkg/crypto/keys/client/list_test.go | 2 +- tm2/pkg/crypto/keys/client/maketx.go | 146 ++++++++++-- tm2/pkg/crypto/keys/client/query.go | 41 ++-- tm2/pkg/crypto/keys/client/root.go | 28 +-- tm2/pkg/crypto/keys/client/send.go | 48 ++-- tm2/pkg/crypto/keys/client/sign.go | 84 ++++--- tm2/pkg/crypto/keys/client/sign_test.go | 14 +- tm2/pkg/crypto/keys/client/verify.go | 22 +- tm2/pkg/crypto/keys/client/verify_test.go | 6 +- 32 files changed, 745 insertions(+), 593 deletions(-) create mode 100644 gno.land/pkg/keyscli/README.md create mode 100644 gno.land/pkg/keyscli/addpkg.go rename {tm2/pkg/crypto/keys/client => gno.land/pkg/keyscli}/call.go (63%) create mode 100644 gno.land/pkg/keyscli/maketx.go create mode 100644 gno.land/pkg/keyscli/root.go rename {tm2/pkg/crypto/keys/client => gno.land/pkg/keyscli}/run.go (75%) delete mode 100644 tm2/pkg/crypto/keys/client/addpkg.go diff --git a/contribs/gnokeykc/go.mod b/contribs/gnokeykc/go.mod index 22f07943f39..48d499755d1 100644 --- a/contribs/gnokeykc/go.mod +++ b/contribs/gnokeykc/go.mod @@ -15,9 +15,7 @@ require ( github.com/btcsuite/btcd/btcutil v1.1.3 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.1.1 // indirect - github.com/cockroachdb/apd/v3 v3.2.1 // indirect github.com/danieljoos/wincred v1.2.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/dgraph-io/badger/v3 v3.2103.4 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect @@ -43,13 +41,10 @@ require ( github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect go.etcd.io/bbolt v1.3.8 // indirect go.opencensus.io v0.22.5 // indirect - go.uber.org/atomic v1.7.0 // indirect - go.uber.org/multierr v1.9.0 // indirect golang.org/x/crypto v0.15.0 // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/sys v0.14.0 // indirect golang.org/x/term v0.14.0 // indirect - golang.org/x/tools v0.13.0 // indirect google.golang.org/protobuf v1.31.0 // indirect ) diff --git a/contribs/gnokeykc/go.sum b/contribs/gnokeykc/go.sum index cc1f4dfed88..17d3e0bc075 100644 --- a/contribs/gnokeykc/go.sum +++ b/contribs/gnokeykc/go.sum @@ -35,8 +35,6 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= -github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -123,7 +121,6 @@ github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/linxGnu/grocksdb v1.8.5 h1:Okfk5B1h0ikCYdDM7Tc5yJUS8LTwAmMBq5IPWTmOLPs= @@ -167,7 +164,6 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= @@ -184,10 +180,6 @@ go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= -go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -253,8 +245,6 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/gno.land/cmd/gnokey/main.go b/gno.land/cmd/gnokey/main.go index 1dd61f22793..a64358ad5d8 100644 --- a/gno.land/cmd/gnokey/main.go +++ b/gno.land/cmd/gnokey/main.go @@ -4,6 +4,7 @@ import ( "context" "os" + "github.com/gnolang/gno/gno.land/pkg/keyscli" "github.com/gnolang/gno/gnovm/pkg/gnoenv" "github.com/gnolang/gno/tm2/pkg/commands" "github.com/gnolang/gno/tm2/pkg/crypto/keys/client" @@ -15,6 +16,6 @@ func main() { Remote: "127.0.0.1:26657", } - cmd := client.NewRootCmdWithBaseConfig(commands.NewDefaultIO(), baseCfg) + cmd := keyscli.NewRootCmd(commands.NewDefaultIO(), baseCfg) cmd.Execute(context.Background(), os.Args[1:]) } diff --git a/gno.land/pkg/integration/testing_integration.go b/gno.land/pkg/integration/testing_integration.go index a56a0948c31..8974ea75637 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/gno.land/pkg/keyscli" "github.com/gnolang/gno/gnovm/pkg/gnoenv" "github.com/gnolang/gno/tm2/pkg/bft/node" "github.com/gnolang/gno/tm2/pkg/commands" @@ -214,7 +215,7 @@ func setupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { io := commands.NewTestIO() io.SetOut(commands.WriteNopCloser(ts.Stdout())) io.SetErr(commands.WriteNopCloser(ts.Stderr())) - cmd := client.NewRootCmd(io) + cmd := keyscli.NewRootCmd(io, client.DefaultBaseOptions) io.SetIn(strings.NewReader("\n")) // Inject empty password to stdin. defaultArgs := []string{ diff --git a/gno.land/pkg/keyscli/README.md b/gno.land/pkg/keyscli/README.md new file mode 100644 index 00000000000..a6d27fa4d40 --- /dev/null +++ b/gno.land/pkg/keyscli/README.md @@ -0,0 +1,12 @@ +## keycli + +`keycli` is an extension of `tm2/keys/client`, enhancing its functionality. It provides the following features: + +- **addpkg**: Allows you to upload a new package to the blockchain. +- **run**: Execute Gno code by invoking the main() function from the target package. +- **call**: Executes a single function call within a Realm. +- **maketx**: Compose a transaction (tx) document to sign (and possibly broadcast). + +--- + +Most of these features have been extracted from `tm2/keys/client` to ensure that `tm2` remains completely independent of `gnovm` and `gno.land`. For more detailed information regarding this change, please refer to [PR#1483](https://github.com/gnolang/gno/pull/1483) diff --git a/gno.land/pkg/keyscli/addpkg.go b/gno.land/pkg/keyscli/addpkg.go new file mode 100644 index 00000000000..1882f6de3d0 --- /dev/null +++ b/gno.land/pkg/keyscli/addpkg.go @@ -0,0 +1,138 @@ +package keyscli + +import ( + "context" + "flag" + "fmt" + + "github.com/gnolang/gno/gno.land/pkg/sdk/vm" + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/tm2/pkg/amino" + "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/errors" + "github.com/gnolang/gno/tm2/pkg/std" +) + +type MakeAddPkgCfg struct { + RootCfg *client.MakeTxCfg + + PkgPath string + PkgDir string + Deposit string +} + +func NewMakeAddPkgCmd(rootCfg *client.MakeTxCfg, io commands.IO) *commands.Command { + cfg := &MakeAddPkgCfg{ + RootCfg: rootCfg, + } + + return commands.NewCommand( + commands.Metadata{ + Name: "addpkg", + ShortUsage: "addpkg [flags] ", + ShortHelp: "Uploads a new package", + }, + cfg, + func(_ context.Context, args []string) error { + return execMakeAddPkg(cfg, args, io) + }, + ) +} + +func (c *MakeAddPkgCfg) RegisterFlags(fs *flag.FlagSet) { + fs.StringVar( + &c.PkgPath, + "pkgpath", + "", + "package path (required)", + ) + + fs.StringVar( + &c.PkgDir, + "pkgdir", + "", + "path to package files (required)", + ) + + fs.StringVar( + &c.Deposit, + "deposit", + "", + "deposit coins", + ) +} + +func execMakeAddPkg(cfg *MakeAddPkgCfg, args []string, io commands.IO) error { + if cfg.PkgPath == "" { + return errors.New("pkgpath not specified") + } + if cfg.PkgDir == "" { + return errors.New("pkgdir not specified") + } + + if len(args) != 1 { + return flag.ErrHelp + } + + // read account pubkey. + nameOrBech32 := args[0] + kb, err := keys.NewKeyBaseFromDir(cfg.RootCfg.RootCfg.Home) + if err != nil { + return err + } + info, err := kb.GetByNameOrAddress(nameOrBech32) + if err != nil { + return err + } + creator := info.GetAddress() + // info.GetPubKey() + + // parse deposit. + deposit, err := std.ParseCoins(cfg.Deposit) + if err != nil { + panic(err) + } + + // open files in directory as MemPackage. + memPkg := gno.ReadMemPackage(cfg.PkgDir, cfg.PkgPath) + if memPkg.IsEmpty() { + panic(fmt.Sprintf("found an empty package %q", cfg.PkgPath)) + } + + // precompile and validate syntax + err = gno.PrecompileAndCheckMempkg(memPkg) + if err != nil { + panic(err) + } + + // parse gas wanted & fee. + gaswanted := cfg.RootCfg.GasWanted + gasfee, err := std.ParseCoin(cfg.RootCfg.GasFee) + if err != nil { + panic(err) + } + // construct msg & tx and marshal. + msg := vm.MsgAddPackage{ + Creator: creator, + Package: memPkg, + Deposit: deposit, + } + tx := std.Tx{ + Msgs: []std.Msg{msg}, + Fee: std.NewFee(gaswanted, gasfee), + Signatures: nil, + Memo: cfg.RootCfg.Memo, + } + + if cfg.RootCfg.Broadcast { + err := client.ExecSignAndBroadcast(cfg.RootCfg, args, tx, io) + if err != nil { + return err + } + } else { + fmt.Println(string(amino.MustMarshalJSON(tx))) + } + return nil +} diff --git a/tm2/pkg/crypto/keys/client/call.go b/gno.land/pkg/keyscli/call.go similarity index 63% rename from tm2/pkg/crypto/keys/client/call.go rename to gno.land/pkg/keyscli/call.go index 6f9c9d52f5f..5afbdf457b8 100644 --- a/tm2/pkg/crypto/keys/client/call.go +++ b/gno.land/pkg/keyscli/call.go @@ -1,4 +1,4 @@ -package client +package keyscli import ( "context" @@ -9,22 +9,23 @@ import ( "github.com/gnolang/gno/tm2/pkg/amino" "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/errors" "github.com/gnolang/gno/tm2/pkg/std" ) -type callCfg struct { - rootCfg *makeTxCfg +type MakeCallCfg struct { + RootCfg *client.MakeTxCfg - send string - pkgPath string - funcName string - args commands.StringArr + Send string + PkgPath string + FuncName string + Args commands.StringArr } -func newCallCmd(rootCfg *makeTxCfg, io commands.IO) *commands.Command { - cfg := &callCfg{ - rootCfg: rootCfg, +func NewMakeCallCmd(rootCfg *client.MakeTxCfg, io commands.IO) *commands.Command { + cfg := &MakeCallCfg{ + RootCfg: rootCfg, } return commands.NewCommand( @@ -35,63 +36,63 @@ func newCallCmd(rootCfg *makeTxCfg, io commands.IO) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execCall(cfg, args, io) + return execMakeCall(cfg, args, io) }, ) } -func (c *callCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *MakeCallCfg) RegisterFlags(fs *flag.FlagSet) { fs.StringVar( - &c.send, + &c.Send, "send", "", "send amount", ) fs.StringVar( - &c.pkgPath, + &c.PkgPath, "pkgpath", "", "package path (required)", ) fs.StringVar( - &c.funcName, + &c.FuncName, "func", "", "contract to call (required)", ) fs.Var( - &c.args, + &c.Args, "args", "arguments to contract", ) } -func execCall(cfg *callCfg, args []string, io commands.IO) error { - if cfg.pkgPath == "" { +func execMakeCall(cfg *MakeCallCfg, args []string, io commands.IO) error { + if cfg.PkgPath == "" { return errors.New("pkgpath not specified") } - if cfg.funcName == "" { + if cfg.FuncName == "" { return errors.New("func not specified") } if len(args) != 1 { return flag.ErrHelp } - if cfg.rootCfg.gasWanted == 0 { + if cfg.RootCfg.GasWanted == 0 { return errors.New("gas-wanted not specified") } - if cfg.rootCfg.gasFee == "" { + if cfg.RootCfg.GasFee == "" { return errors.New("gas-fee not specified") } // read statement. - fnc := cfg.funcName + fnc := cfg.FuncName // read account pubkey. nameOrBech32 := args[0] - kb, err := keys.NewKeyBaseFromDir(cfg.rootCfg.rootCfg.Home) + kb, err := keys.NewKeyBaseFromDir(cfg.RootCfg.RootCfg.Home) if err != nil { return err } @@ -103,14 +104,14 @@ func execCall(cfg *callCfg, args []string, io commands.IO) error { // info.GetPubKey() // Parse send amount. - send, err := std.ParseCoins(cfg.send) + send, err := std.ParseCoins(cfg.Send) if err != nil { return errors.Wrap(err, "parsing send coins") } // parse gas wanted & fee. - gaswanted := cfg.rootCfg.gasWanted - gasfee, err := std.ParseCoin(cfg.rootCfg.gasFee) + gaswanted := cfg.RootCfg.GasWanted + gasfee, err := std.ParseCoin(cfg.RootCfg.GasFee) if err != nil { return errors.Wrap(err, "parsing gas fee coin") } @@ -119,19 +120,19 @@ func execCall(cfg *callCfg, args []string, io commands.IO) error { msg := vm.MsgCall{ Caller: caller, Send: send, - PkgPath: cfg.pkgPath, + PkgPath: cfg.PkgPath, Func: fnc, - Args: cfg.args, + Args: cfg.Args, } tx := std.Tx{ Msgs: []std.Msg{msg}, Fee: std.NewFee(gaswanted, gasfee), Signatures: nil, - Memo: cfg.rootCfg.memo, + Memo: cfg.RootCfg.Memo, } - if cfg.rootCfg.broadcast { - err := signAndBroadcast(cfg.rootCfg, args, tx, io) + if cfg.RootCfg.Broadcast { + err := client.ExecSignAndBroadcast(cfg.RootCfg, args, tx, io) if err != nil { return err } diff --git a/gno.land/pkg/keyscli/maketx.go b/gno.land/pkg/keyscli/maketx.go new file mode 100644 index 00000000000..3aa54546863 --- /dev/null +++ b/gno.land/pkg/keyscli/maketx.go @@ -0,0 +1,83 @@ +package keyscli + +import ( + "flag" + + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/crypto/keys/client" +) + +type MakeTxCfg struct { + RootCfg *client.BaseCfg + + GasWanted int64 + GasFee string + Memo string + + Broadcast bool + ChainID string +} + +func NewMakeTxCmd(rootCfg *client.BaseCfg, io commands.IO) *commands.Command { + cfg := &client.MakeTxCfg{ + RootCfg: rootCfg, + } + + cmd := commands.NewCommand( + commands.Metadata{ + Name: "maketx", + ShortUsage: " [flags] [...]", + ShortHelp: "Composes a tx document to sign", + }, + cfg, + commands.HelpExec, + ) + + cmd.AddSubCommands( + client.NewMakeSendCmd(cfg, io), + + // custom commands + NewMakeAddPkgCmd(cfg, io), + NewMakeCallCmd(cfg, io), + NewMakeRunCmd(cfg, io), + ) + + return cmd +} + +func (c *MakeTxCfg) RegisterFlags(fs *flag.FlagSet) { + fs.Int64Var( + &c.GasWanted, + "gas-wanted", + 0, + "gas requested for tx", + ) + + fs.StringVar( + &c.GasFee, + "gas-fee", + "", + "gas payment fee", + ) + + fs.StringVar( + &c.Memo, + "memo", + "", + "any descriptive text", + ) + + fs.BoolVar( + &c.Broadcast, + "broadcast", + false, + "sign and broadcast", + ) + + fs.StringVar( + &c.ChainID, + "chainid", + "dev", + "chainid to sign for (only useful if --broadcast)", + ) +} diff --git a/gno.land/pkg/keyscli/root.go b/gno.land/pkg/keyscli/root.go new file mode 100644 index 00000000000..dc5a4f1f9af --- /dev/null +++ b/gno.land/pkg/keyscli/root.go @@ -0,0 +1,47 @@ +// Dedicated to my love, Lexi. +package keyscli + +import ( + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/crypto/keys/client" + + "github.com/peterbourgon/ff/v3" + "github.com/peterbourgon/ff/v3/fftoml" +) + +func NewRootCmd(io commands.IO, base client.BaseOptions) *commands.Command { + cfg := &client.BaseCfg{ + BaseOptions: base, + } + + cmd := commands.NewCommand( + commands.Metadata{ + ShortUsage: " [flags] [...]", + LongHelp: "Manages private keys for the node", + Options: []ff.Option{ + ff.WithConfigFileFlag("config"), + ff.WithConfigFileParser(fftoml.Parser), + }, + }, + cfg, + commands.HelpExec, + ) + + cmd.AddSubCommands( + client.NewAddCmd(cfg, io), + client.NewDeleteCmd(cfg, io), + client.NewGenerateCmd(cfg, io), + client.NewExportCmd(cfg, io), + client.NewImportCmd(cfg, io), + client.NewListCmd(cfg, io), + client.NewSignCmd(cfg, io), + client.NewVerifyCmd(cfg, io), + client.NewQueryCmd(cfg, io), + client.NewBroadcastCmd(cfg, io), + + // Custom MakeTX command + NewMakeTxCmd(cfg, io), + ) + + return cmd +} diff --git a/tm2/pkg/crypto/keys/client/run.go b/gno.land/pkg/keyscli/run.go similarity index 75% rename from tm2/pkg/crypto/keys/client/run.go rename to gno.land/pkg/keyscli/run.go index 1ae702c990e..e04452dde09 100644 --- a/tm2/pkg/crypto/keys/client/run.go +++ b/gno.land/pkg/keyscli/run.go @@ -1,10 +1,10 @@ -package client +package keyscli import ( "context" "flag" "fmt" - "io/ioutil" + "io" "os" "github.com/gnolang/gno/gno.land/pkg/sdk/vm" @@ -12,17 +12,18 @@ import ( "github.com/gnolang/gno/tm2/pkg/amino" "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/errors" "github.com/gnolang/gno/tm2/pkg/std" ) -type runCfg struct { - rootCfg *makeTxCfg +type MakeRunCfg struct { + RootCfg *client.MakeTxCfg } -func newRunCmd(rootCfg *makeTxCfg, io commands.IO) *commands.Command { - cfg := &runCfg{ - rootCfg: rootCfg, +func NewMakeRunCmd(rootCfg *client.MakeTxCfg, cmdio commands.IO) *commands.Command { + cfg := &MakeRunCfg{ + RootCfg: rootCfg, } return commands.NewCommand( @@ -33,21 +34,21 @@ func newRunCmd(rootCfg *makeTxCfg, io commands.IO) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return runRun(cfg, args, io) + return execMakeRun(cfg, args, cmdio) }, ) } -func (c *runCfg) RegisterFlags(fs *flag.FlagSet) {} +func (c *MakeRunCfg) RegisterFlags(fs *flag.FlagSet) {} -func runRun(cfg *runCfg, args []string, io commands.IO) error { +func execMakeRun(cfg *MakeRunCfg, args []string, cmdio commands.IO) error { if len(args) != 2 { return flag.ErrHelp } - if cfg.rootCfg.gasWanted == 0 { + if cfg.RootCfg.GasWanted == 0 { return errors.New("gas-wanted not specified") } - if cfg.rootCfg.gasFee == "" { + if cfg.RootCfg.GasFee == "" { return errors.New("gas-fee not specified") } @@ -55,7 +56,7 @@ func runRun(cfg *runCfg, args []string, io commands.IO) error { sourcePath := args[1] // can be a file path, a dir path, or '-' for stdin // read account pubkey. - kb, err := keys.NewKeyBaseFromDir(cfg.rootCfg.rootCfg.Home) + kb, err := keys.NewKeyBaseFromDir(cfg.RootCfg.RootCfg.Home) if err != nil { return err } @@ -66,15 +67,15 @@ func runRun(cfg *runCfg, args []string, io commands.IO) error { caller := info.GetAddress() // parse gas wanted & fee. - gaswanted := cfg.rootCfg.gasWanted - gasfee, err := std.ParseCoin(cfg.rootCfg.gasFee) + gaswanted := cfg.RootCfg.GasWanted + gasfee, err := std.ParseCoin(cfg.RootCfg.GasFee) if err != nil { return errors.Wrap(err, "parsing gas fee coin") } memPkg := &std.MemPackage{} if sourcePath == "-" { // stdin - data, err := ioutil.ReadAll(io.In()) + data, err := io.ReadAll(cmdio.In()) if err != nil { return fmt.Errorf("could not read stdin: %w", err) } @@ -124,11 +125,11 @@ func runRun(cfg *runCfg, args []string, io commands.IO) error { Msgs: []std.Msg{msg}, Fee: std.NewFee(gaswanted, gasfee), Signatures: nil, - Memo: cfg.rootCfg.memo, + Memo: cfg.RootCfg.Memo, } - if cfg.rootCfg.broadcast { - err := signAndBroadcast(cfg.rootCfg, args, tx, io) + if cfg.RootCfg.Broadcast { + err := client.ExecSignAndBroadcast(cfg.RootCfg, args, tx, cmdio) if err != nil { return err } diff --git a/tm2/pkg/crypto/keys/client/add.go b/tm2/pkg/crypto/keys/client/add.go index 71dc6f03090..49436220974 100644 --- a/tm2/pkg/crypto/keys/client/add.go +++ b/tm2/pkg/crypto/keys/client/add.go @@ -14,24 +14,24 @@ import ( "github.com/gnolang/gno/tm2/pkg/crypto/multisig" ) -type addCfg struct { - rootCfg *baseCfg - - multisig commands.StringArr - multisigThreshold int - noSort bool - publicKey string - useLedger bool - recover bool - noBackup bool - dryRun bool - account uint64 - index uint64 +type AddCfg struct { + RootCfg *BaseCfg + + Multisig commands.StringArr + MultisigThreshold int + NoSort bool + PublicKey string + UseLedger bool + Recover bool + NoBackup bool + DryRun bool + Account uint64 + Index uint64 } -func newAddCmd(rootCfg *baseCfg, io commands.IO) *commands.Command { - cfg := &addCfg{ - rootCfg: rootCfg, +func NewAddCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command { + cfg := &AddCfg{ + RootCfg: rootCfg, } return commands.NewCommand( @@ -47,71 +47,71 @@ func newAddCmd(rootCfg *baseCfg, io commands.IO) *commands.Command { ) } -func (c *addCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *AddCfg) RegisterFlags(fs *flag.FlagSet) { fs.Var( - &c.multisig, + &c.Multisig, "multisig", "Construct and store a multisig public key (implies --pubkey)", ) fs.IntVar( - &c.multisigThreshold, + &c.MultisigThreshold, "threshold", 1, "K out of N required signatures. For use in conjunction with --multisig", ) fs.BoolVar( - &c.noSort, + &c.NoSort, "nosort", false, "Keys passed to --multisig are taken in the order they're supplied", ) fs.StringVar( - &c.publicKey, + &c.PublicKey, "pubkey", "", "Parse a public key in bech32 format and save it to disk", ) fs.BoolVar( - &c.useLedger, + &c.UseLedger, "ledger", false, "Store a local reference to a private key on a Ledger device", ) fs.BoolVar( - &c.recover, + &c.Recover, "recover", false, "Provide seed phrase to recover existing key instead of creating", ) fs.BoolVar( - &c.noBackup, + &c.NoBackup, "nobackup", false, "Don't print out seed phrase (if others are watching the terminal)", ) fs.BoolVar( - &c.dryRun, + &c.DryRun, "dryrun", false, "Perform action, but don't add key to local keystore", ) fs.Uint64Var( - &c.account, + &c.Account, "account", 0, "Account number for HD derivation", ) fs.Uint64Var( - &c.index, + &c.Index, "index", 0, "Address index number for HD derivation", @@ -131,7 +131,7 @@ input output - armor encrypted private key (saved to file) */ -func execAdd(cfg *addCfg, args []string, io commands.IO) error { +func execAdd(cfg *AddCfg, args []string, io commands.IO) error { var ( kb keys.Keybase err error @@ -143,15 +143,15 @@ func execAdd(cfg *addCfg, args []string, io commands.IO) error { } name := args[0] - showMnemonic := !cfg.noBackup + showMnemonic := !cfg.NoBackup - if cfg.dryRun { + if cfg.DryRun { // we throw this away, so don't enforce args, // we want to get a new random seed phrase quickly kb = keys.NewInMemory() encryptPassword = DryRunKeyPass } else { - kb, err = keys.NewKeyBaseFromDir(cfg.rootCfg.Home) + kb, err = keys.NewKeyBaseFromDir(cfg.RootCfg.Home) if err != nil { return err } @@ -167,11 +167,11 @@ func execAdd(cfg *addCfg, args []string, io commands.IO) error { } } - multisigKeys := cfg.multisig + multisigKeys := cfg.Multisig if len(multisigKeys) != 0 { var pks []crypto.PubKey - multisigThreshold := cfg.multisigThreshold + multisigThreshold := cfg.MultisigThreshold if err := keys.ValidateMultisigThreshold(multisigThreshold, len(multisigKeys)); err != nil { return err } @@ -185,7 +185,7 @@ func execAdd(cfg *addCfg, args []string, io commands.IO) error { } // Handle --nosort - if !cfg.noSort { + if !cfg.NoSort { sort.Slice(pks, func(i, j int) bool { return pks[i].Address().Compare(pks[j].Address()) < 0 }) @@ -201,13 +201,13 @@ func execAdd(cfg *addCfg, args []string, io commands.IO) error { } // ask for a password when generating a local key - if cfg.publicKey == "" && !cfg.useLedger { + if cfg.PublicKey == "" && !cfg.UseLedger { encryptPassword, err = io.GetCheckPassword( [2]string{ "Enter a passphrase to encrypt your key to disk:", "Repeat the passphrase:", }, - cfg.rootCfg.InsecurePasswordStdin, + cfg.RootCfg.InsecurePasswordStdin, ) if err != nil { return err @@ -215,8 +215,8 @@ func execAdd(cfg *addCfg, args []string, io commands.IO) error { } } - if cfg.publicKey != "" { - pk, err := crypto.PubKeyFromBech32(cfg.publicKey) + if cfg.PublicKey != "" { + pk, err := crypto.PubKeyFromBech32(cfg.PublicKey) if err != nil { return err } @@ -227,11 +227,11 @@ func execAdd(cfg *addCfg, args []string, io commands.IO) error { return nil } - account := cfg.account - index := cfg.index + account := cfg.Account + index := cfg.Index // If we're using ledger, only thing we need is the path and the bech32 prefix. - if cfg.useLedger { + if cfg.UseLedger { bech32PrefixAddr := crypto.Bech32AddrPrefix info, err := kb.CreateLedger(name, keys.Secp256k1, bech32PrefixAddr, uint32(account), uint32(index)) if err != nil { @@ -245,7 +245,7 @@ func execAdd(cfg *addCfg, args []string, io commands.IO) error { var mnemonic string const bip39Passphrase string = "" // XXX research. - if cfg.recover { + if cfg.Recover { bip39Message := "Enter your bip39 mnemonic" mnemonic, err = io.GetString(bip39Message) if err != nil { @@ -270,7 +270,7 @@ func execAdd(cfg *addCfg, args []string, io commands.IO) error { } // Recover key from seed passphrase - if cfg.recover { + if cfg.Recover { // Hide mnemonic from output showMnemonic = false mnemonic = "" diff --git a/tm2/pkg/crypto/keys/client/add_test.go b/tm2/pkg/crypto/keys/client/add_test.go index 94cb945f113..4110ea32c9a 100644 --- a/tm2/pkg/crypto/keys/client/add_test.go +++ b/tm2/pkg/crypto/keys/client/add_test.go @@ -20,8 +20,8 @@ func Test_execAddBasic(t *testing.T) { assert.NotNil(t, kbHome) defer kbCleanUp() - cfg := &addCfg{ - rootCfg: &baseCfg{ + cfg := &AddCfg{ + RootCfg: &BaseCfg{ BaseOptions: BaseOptions{ InsecurePasswordStdin: true, Home: kbHome, @@ -59,13 +59,13 @@ func Test_execAddPublicKey(t *testing.T) { assert.NotNil(t, kbHome) defer kbCleanUp() - cfg := &addCfg{ - rootCfg: &baseCfg{ + cfg := &AddCfg{ + RootCfg: &BaseCfg{ BaseOptions: BaseOptions{ Home: kbHome, }, }, - publicKey: test2PubkeyBech32, // test2 account + PublicKey: test2PubkeyBech32, // test2 account } if err := execAdd(cfg, []string{"test2"}, nil); err != nil { @@ -80,14 +80,14 @@ func Test_execAddRecover(t *testing.T) { assert.NotNil(t, kbHome) defer kbCleanUp() - cfg := &addCfg{ - rootCfg: &baseCfg{ + cfg := &AddCfg{ + RootCfg: &BaseCfg{ BaseOptions: BaseOptions{ InsecurePasswordStdin: true, Home: kbHome, }, }, - recover: true, // init test2 account + Recover: true, // init test2 account } test2Name := "test2" diff --git a/tm2/pkg/crypto/keys/client/addpkg.go b/tm2/pkg/crypto/keys/client/addpkg.go deleted file mode 100644 index 5bbd3f08ad0..00000000000 --- a/tm2/pkg/crypto/keys/client/addpkg.go +++ /dev/null @@ -1,222 +0,0 @@ -package client - -// TODO: move most of the logic in ROOT/gno.land/... - -import ( - "context" - "flag" - "fmt" - - "github.com/gnolang/gno/gno.land/pkg/sdk/vm" - gno "github.com/gnolang/gno/gnovm/pkg/gnolang" - "github.com/gnolang/gno/tm2/pkg/amino" - "github.com/gnolang/gno/tm2/pkg/commands" - "github.com/gnolang/gno/tm2/pkg/crypto/keys" - "github.com/gnolang/gno/tm2/pkg/errors" - "github.com/gnolang/gno/tm2/pkg/std" -) - -type addPkgCfg struct { - rootCfg *makeTxCfg - - pkgPath string - pkgDir string - deposit string -} - -func newAddPkgCmd(rootCfg *makeTxCfg, io commands.IO) *commands.Command { - cfg := &addPkgCfg{ - rootCfg: rootCfg, - } - - return commands.NewCommand( - commands.Metadata{ - Name: "addpkg", - ShortUsage: "addpkg [flags] ", - ShortHelp: "Uploads a new package", - }, - cfg, - func(_ context.Context, args []string) error { - return execAddPkg(cfg, args, io) - }, - ) -} - -func (c *addPkgCfg) RegisterFlags(fs *flag.FlagSet) { - fs.StringVar( - &c.pkgPath, - "pkgpath", - "", - "package path (required)", - ) - - fs.StringVar( - &c.pkgDir, - "pkgdir", - "", - "path to package files (required)", - ) - - fs.StringVar( - &c.deposit, - "deposit", - "", - "deposit coins", - ) -} - -func execAddPkg(cfg *addPkgCfg, args []string, io commands.IO) error { - if cfg.pkgPath == "" { - return errors.New("pkgpath not specified") - } - if cfg.pkgDir == "" { - return errors.New("pkgdir not specified") - } - - if len(args) != 1 { - return flag.ErrHelp - } - - // read account pubkey. - nameOrBech32 := args[0] - kb, err := keys.NewKeyBaseFromDir(cfg.rootCfg.rootCfg.Home) - if err != nil { - return err - } - info, err := kb.GetByNameOrAddress(nameOrBech32) - if err != nil { - return err - } - creator := info.GetAddress() - // info.GetPubKey() - - // parse deposit. - deposit, err := std.ParseCoins(cfg.deposit) - if err != nil { - panic(err) - } - - // open files in directory as MemPackage. - memPkg := gno.ReadMemPackage(cfg.pkgDir, cfg.pkgPath) - if memPkg.IsEmpty() { - panic(fmt.Sprintf("found an empty package %q", cfg.pkgPath)) - } - - // precompile and validate syntax - err = gno.PrecompileAndCheckMempkg(memPkg) - if err != nil { - panic(err) - } - - // parse gas wanted & fee. - gaswanted := cfg.rootCfg.gasWanted - gasfee, err := std.ParseCoin(cfg.rootCfg.gasFee) - if err != nil { - panic(err) - } - // construct msg & tx and marshal. - msg := vm.MsgAddPackage{ - Creator: creator, - Package: memPkg, - Deposit: deposit, - } - tx := std.Tx{ - Msgs: []std.Msg{msg}, - Fee: std.NewFee(gaswanted, gasfee), - Signatures: nil, - Memo: cfg.rootCfg.memo, - } - - if cfg.rootCfg.broadcast { - err := signAndBroadcast(cfg.rootCfg, args, tx, io) - if err != nil { - return err - } - } else { - fmt.Println(string(amino.MustMarshalJSON(tx))) - } - return nil -} - -func signAndBroadcast( - cfg *makeTxCfg, - args []string, - tx std.Tx, - io commands.IO, -) error { - baseopts := cfg.rootCfg - txopts := cfg - - // query account - nameOrBech32 := args[0] - kb, err := keys.NewKeyBaseFromDir(baseopts.Home) - if err != nil { - return err - } - info, err := kb.GetByNameOrAddress(nameOrBech32) - if err != nil { - return err - } - accountAddr := info.GetAddress() - - qopts := &queryCfg{ - rootCfg: baseopts, - path: fmt.Sprintf("auth/accounts/%s", accountAddr), - } - qres, err := queryHandler(qopts) - if err != nil { - return errors.Wrap(err, "query account") - } - var qret struct{ BaseAccount std.BaseAccount } - err = amino.UnmarshalJSON(qres.Response.Data, &qret) - if err != nil { - return err - } - - // sign tx - accountNumber := qret.BaseAccount.AccountNumber - sequence := qret.BaseAccount.Sequence - sopts := &signCfg{ - rootCfg: baseopts, - sequence: sequence, - accountNumber: accountNumber, - chainID: txopts.chainID, - nameOrBech32: nameOrBech32, - txJSON: amino.MustMarshalJSON(tx), - } - if baseopts.Quiet { - sopts.pass, err = io.GetPassword("", baseopts.InsecurePasswordStdin) - } else { - sopts.pass, err = io.GetPassword("Enter password.", baseopts.InsecurePasswordStdin) - } - if err != nil { - return err - } - - signedTx, err := SignHandler(sopts) - if err != nil { - return errors.Wrap(err, "sign tx") - } - - // broadcast signed tx - bopts := &broadcastCfg{ - rootCfg: baseopts, - tx: signedTx, - } - bres, err := broadcastHandler(bopts) - if err != nil { - return errors.Wrap(err, "broadcast tx") - } - if bres.CheckTx.IsErr() { - return errors.Wrap(bres.CheckTx.Error, "check transaction failed: log:%s", bres.CheckTx.Log) - } - if bres.DeliverTx.IsErr() { - return errors.Wrap(bres.DeliverTx.Error, "deliver transaction failed: log:%s", bres.DeliverTx.Log) - } - io.Println(string(bres.DeliverTx.Data)) - io.Println("OK!") - io.Println("GAS WANTED:", bres.DeliverTx.GasWanted) - io.Println("GAS USED: ", bres.DeliverTx.GasUsed) - - return nil -} diff --git a/tm2/pkg/crypto/keys/client/broadcast.go b/tm2/pkg/crypto/keys/client/broadcast.go index 9c05f2c43b3..3443f4678c6 100644 --- a/tm2/pkg/crypto/keys/client/broadcast.go +++ b/tm2/pkg/crypto/keys/client/broadcast.go @@ -14,18 +14,18 @@ import ( "github.com/gnolang/gno/tm2/pkg/std" ) -type broadcastCfg struct { - rootCfg *baseCfg +type BroadcastCfg struct { + RootCfg *BaseCfg - dryRun bool + DryRun bool // internal tx *std.Tx } -func newBroadcastCmd(rootCfg *baseCfg, io commands.IO) *commands.Command { - cfg := &broadcastCfg{ - rootCfg: rootCfg, +func NewBroadcastCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command { + cfg := &BroadcastCfg{ + RootCfg: rootCfg, } return commands.NewCommand( @@ -41,16 +41,16 @@ func newBroadcastCmd(rootCfg *baseCfg, io commands.IO) *commands.Command { ) } -func (c *broadcastCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *BroadcastCfg) RegisterFlags(fs *flag.FlagSet) { fs.BoolVar( - &c.dryRun, + &c.DryRun, "dry-run", false, "perform a dry-run broadcast", ) } -func execBroadcast(cfg *broadcastCfg, args []string, io commands.IO) error { +func execBroadcast(cfg *BroadcastCfg, args []string, io commands.IO) error { if len(args) != 1 { return flag.ErrHelp } @@ -67,7 +67,7 @@ func execBroadcast(cfg *broadcastCfg, args []string, io commands.IO) error { } cfg.tx = &tx - res, err := broadcastHandler(cfg) + res, err := BroadcastHandler(cfg) if err != nil { return err } @@ -85,12 +85,12 @@ func execBroadcast(cfg *broadcastCfg, args []string, io commands.IO) error { return nil } -func broadcastHandler(cfg *broadcastCfg) (*ctypes.ResultBroadcastTxCommit, error) { +func BroadcastHandler(cfg *BroadcastCfg) (*ctypes.ResultBroadcastTxCommit, error) { if cfg.tx == nil { return nil, errors.New("invalid tx") } - remote := cfg.rootCfg.Remote + remote := cfg.RootCfg.Remote if remote == "" || remote == "y" { return nil, errors.New("missing remote url") } @@ -102,8 +102,8 @@ func broadcastHandler(cfg *broadcastCfg) (*ctypes.ResultBroadcastTxCommit, error cli := client.NewHTTP(remote, "/websocket") - if cfg.dryRun { - return simulateTx(cli, bz) + if cfg.DryRun { + return SimulateTx(cli, bz) } bres, err := cli.BroadcastTxCommit(bz) @@ -114,7 +114,7 @@ func broadcastHandler(cfg *broadcastCfg) (*ctypes.ResultBroadcastTxCommit, error return bres, nil } -func simulateTx(cli client.ABCIClient, tx []byte) (*ctypes.ResultBroadcastTxCommit, error) { +func SimulateTx(cli client.ABCIClient, tx []byte) (*ctypes.ResultBroadcastTxCommit, error) { bres, err := cli.ABCIQuery(".app/simulate", tx) if err != nil { return nil, errors.Wrap(err, "simulate tx") diff --git a/tm2/pkg/crypto/keys/client/delete.go b/tm2/pkg/crypto/keys/client/delete.go index cf65b8fc60a..69c185c3e75 100644 --- a/tm2/pkg/crypto/keys/client/delete.go +++ b/tm2/pkg/crypto/keys/client/delete.go @@ -9,16 +9,16 @@ import ( "github.com/gnolang/gno/tm2/pkg/crypto/keys" ) -type deleteCfg struct { - rootCfg *baseCfg +type DeleteCfg struct { + RootCfg *BaseCfg - yes bool - force bool + Yes bool + Force bool } -func newDeleteCmd(rootCfg *baseCfg, io commands.IO) *commands.Command { - cfg := &deleteCfg{ - rootCfg: rootCfg, +func NewDeleteCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command { + cfg := &DeleteCfg{ + RootCfg: rootCfg, } return commands.NewCommand( @@ -34,30 +34,30 @@ func newDeleteCmd(rootCfg *baseCfg, io commands.IO) *commands.Command { ) } -func (c *deleteCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *DeleteCfg) RegisterFlags(fs *flag.FlagSet) { fs.BoolVar( - &c.yes, + &c.Yes, "yes", false, "skip confirmation prompt", ) fs.BoolVar( - &c.force, + &c.Force, "force", false, "remove key unconditionally", ) } -func execDelete(cfg *deleteCfg, args []string, io commands.IO) error { +func execDelete(cfg *DeleteCfg, args []string, io commands.IO) error { if len(args) != 1 { return flag.ErrHelp } nameOrBech32 := args[0] - kb, err := keys.NewKeyBaseFromDir(cfg.rootCfg.Home) + kb, err := keys.NewKeyBaseFromDir(cfg.RootCfg.Home) if err != nil { return err } @@ -68,7 +68,7 @@ func execDelete(cfg *deleteCfg, args []string, io commands.IO) error { } if info.GetType() == keys.TypeLedger || info.GetType() == keys.TypeOffline { - if !cfg.yes { + if !cfg.Yes { if err := confirmDeletion(io); err != nil { return err } @@ -83,11 +83,11 @@ func execDelete(cfg *deleteCfg, args []string, io commands.IO) error { } // skip passphrase check if run with --force - skipPass := cfg.force + skipPass := cfg.Force var oldpass string if !skipPass { msg := "DANGER - enter password to permanently delete key:" - if oldpass, err = io.GetPassword(msg, cfg.rootCfg.InsecurePasswordStdin); err != nil { + if oldpass, err = io.GetPassword(msg, cfg.RootCfg.InsecurePasswordStdin); err != nil { return err } } diff --git a/tm2/pkg/crypto/keys/client/delete_test.go b/tm2/pkg/crypto/keys/client/delete_test.go index b3f83f1d9a2..a9724ac483b 100644 --- a/tm2/pkg/crypto/keys/client/delete_test.go +++ b/tm2/pkg/crypto/keys/client/delete_test.go @@ -23,8 +23,8 @@ func Test_execDelete(t *testing.T) { defer kbCleanUp() // initialize test options - cfg := &deleteCfg{ - rootCfg: &baseCfg{ + cfg := &DeleteCfg{ + RootCfg: &BaseCfg{ BaseOptions: BaseOptions{ Home: kbHome, InsecurePasswordStdin: true, @@ -75,14 +75,14 @@ func Test_execDelete(t *testing.T) { } // Set config yes = true - cfg = &deleteCfg{ - rootCfg: &baseCfg{ + cfg = &DeleteCfg{ + RootCfg: &BaseCfg{ BaseOptions: BaseOptions{ Home: kbHome, InsecurePasswordStdin: true, }, }, - yes: true, + Yes: true, } _, err = kb.GetByName(fakeKeyName2) diff --git a/tm2/pkg/crypto/keys/client/export.go b/tm2/pkg/crypto/keys/client/export.go index 71955cff5d2..58f533bd3be 100644 --- a/tm2/pkg/crypto/keys/client/export.go +++ b/tm2/pkg/crypto/keys/client/export.go @@ -11,17 +11,17 @@ import ( "github.com/gnolang/gno/tm2/pkg/crypto/keys" ) -type exportCfg struct { - rootCfg *baseCfg +type ExportCfg struct { + RootCfg *BaseCfg - nameOrBech32 string - outputPath string - unsafe bool + NameOrBech32 string + OutputPath string + Unsafe bool } -func newExportCmd(rootCfg *baseCfg, io commands.IO) *commands.Command { - cfg := &exportCfg{ - rootCfg: rootCfg, +func NewExportCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command { + cfg := &ExportCfg{ + RootCfg: rootCfg, } return commands.NewCommand( @@ -37,40 +37,41 @@ func newExportCmd(rootCfg *baseCfg, io commands.IO) *commands.Command { ) } -func (c *exportCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *ExportCfg) RegisterFlags(fs *flag.FlagSet) { fs.StringVar( - &c.nameOrBech32, + &c.NameOrBech32, "key", "", "Name or Bech32 address of the private key", ) fs.StringVar( - &c.outputPath, + &c.OutputPath, "output-path", "", "The desired output path for the armor file", ) fs.BoolVar( - &c.unsafe, + &c.Unsafe, "unsafe", false, "Export the private key armor as unencrypted", ) } -func execExport(cfg *exportCfg, io commands.IO) error { +func execExport(cfg *ExportCfg, io commands.IO) error { // check keyname - if cfg.nameOrBech32 == "" { + if cfg.NameOrBech32 == "" { return errors.New("key to be exported shouldn't be empty") } + // Create a new instance of the key-base - kb, err := keys.NewKeyBaseFromDir(cfg.rootCfg.Home) + kb, err := keys.NewKeyBaseFromDir(cfg.RootCfg.Home) if err != nil { return fmt.Errorf( "unable to create a key base from directory %s, %w", - cfg.rootCfg.Home, + cfg.RootCfg.Home, err, ) } @@ -78,7 +79,7 @@ func execExport(cfg *exportCfg, io commands.IO) error { // Get the key-base decrypt password decryptPassword, err := io.GetPassword( "Enter a passphrase to decrypt your private key from disk:", - cfg.rootCfg.InsecurePasswordStdin, + cfg.RootCfg.InsecurePasswordStdin, ) if err != nil { return fmt.Errorf( @@ -92,10 +93,10 @@ func execExport(cfg *exportCfg, io commands.IO) error { exportErr error ) - if cfg.unsafe { + if cfg.Unsafe { // Generate the unencrypted armor armor, exportErr = kb.ExportPrivKeyUnsafe( - cfg.nameOrBech32, + cfg.NameOrBech32, decryptPassword, ) } else { @@ -105,7 +106,7 @@ func execExport(cfg *exportCfg, io commands.IO) error { "Enter a passphrase to encrypt your private key armor:", "Repeat the passphrase:", }, - cfg.rootCfg.InsecurePasswordStdin, + cfg.RootCfg.InsecurePasswordStdin, ) if err != nil { return fmt.Errorf( @@ -116,7 +117,7 @@ func execExport(cfg *exportCfg, io commands.IO) error { // Generate the encrypted armor armor, exportErr = kb.ExportPrivKey( - cfg.nameOrBech32, + cfg.NameOrBech32, decryptPassword, encryptPassword, ) @@ -131,7 +132,7 @@ func execExport(cfg *exportCfg, io commands.IO) error { // Write the armor to disk if err := os.WriteFile( - cfg.outputPath, + cfg.OutputPath, []byte(armor), 0o644, ); err != nil { @@ -141,7 +142,7 @@ func execExport(cfg *exportCfg, io commands.IO) error { ) } - io.Printfln("Private key armor successfully outputted to %s", cfg.outputPath) + io.Printfln("Private key armor successfully outputted to %s", cfg.OutputPath) return nil } diff --git a/tm2/pkg/crypto/keys/client/export_test.go b/tm2/pkg/crypto/keys/client/export_test.go index 2ff5fe9eb37..dfd7c74ce38 100644 --- a/tm2/pkg/crypto/keys/client/export_test.go +++ b/tm2/pkg/crypto/keys/client/export_test.go @@ -81,16 +81,16 @@ func exportKey( exportOpts testExportKeyOpts, input io.Reader, ) error { - cfg := &exportCfg{ - rootCfg: &baseCfg{ + cfg := &ExportCfg{ + RootCfg: &BaseCfg{ BaseOptions: BaseOptions{ Home: exportOpts.kbHome, InsecurePasswordStdin: true, }, }, - nameOrBech32: exportOpts.keyName, - outputPath: exportOpts.outputPath, - unsafe: exportOpts.unsafe, + NameOrBech32: exportOpts.keyName, + OutputPath: exportOpts.outputPath, + Unsafe: exportOpts.unsafe, } cmdIO := commands.NewTestIO() diff --git a/tm2/pkg/crypto/keys/client/generate.go b/tm2/pkg/crypto/keys/client/generate.go index 04a0ea8947f..bae3b6b42a3 100644 --- a/tm2/pkg/crypto/keys/client/generate.go +++ b/tm2/pkg/crypto/keys/client/generate.go @@ -10,15 +10,15 @@ import ( "github.com/gnolang/gno/tm2/pkg/crypto/bip39" ) -type generateCfg struct { - rootCfg *baseCfg +type GenerateCfg struct { + RootCfg *BaseCfg - customEntropy bool + CustomEntropy bool } -func newGenerateCmd(rootCfg *baseCfg, io commands.IO) *commands.Command { - cfg := &generateCfg{ - rootCfg: rootCfg, +func NewGenerateCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command { + cfg := &GenerateCfg{ + RootCfg: rootCfg, } return commands.NewCommand( @@ -34,17 +34,17 @@ func newGenerateCmd(rootCfg *baseCfg, io commands.IO) *commands.Command { ) } -func (c *generateCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *GenerateCfg) RegisterFlags(fs *flag.FlagSet) { fs.BoolVar( - &c.customEntropy, + &c.CustomEntropy, "entropy", false, "supply custom entropy", ) } -func execGenerate(cfg *generateCfg, args []string, io commands.IO) error { - customEntropy := cfg.customEntropy +func execGenerate(cfg *GenerateCfg, args []string, io commands.IO) error { + customEntropy := cfg.CustomEntropy if len(args) != 0 { return flag.ErrHelp diff --git a/tm2/pkg/crypto/keys/client/generate_test.go b/tm2/pkg/crypto/keys/client/generate_test.go index 516912046b6..25eca1ed628 100644 --- a/tm2/pkg/crypto/keys/client/generate_test.go +++ b/tm2/pkg/crypto/keys/client/generate_test.go @@ -11,8 +11,8 @@ import ( func Test_execGenerateNormal(t *testing.T) { t.Parallel() - cfg := &generateCfg{ - customEntropy: false, + cfg := &GenerateCfg{ + CustomEntropy: false, } err := execGenerate(cfg, []string{}, commands.NewTestIO()) @@ -22,8 +22,8 @@ func Test_execGenerateNormal(t *testing.T) { func Test_execGenerateUser(t *testing.T) { t.Parallel() - cfg := &generateCfg{ - customEntropy: true, + cfg := &GenerateCfg{ + CustomEntropy: true, } io := commands.NewTestIO() diff --git a/tm2/pkg/crypto/keys/client/import.go b/tm2/pkg/crypto/keys/client/import.go index 9a3f0377782..cace9bc0964 100644 --- a/tm2/pkg/crypto/keys/client/import.go +++ b/tm2/pkg/crypto/keys/client/import.go @@ -11,17 +11,17 @@ import ( "github.com/gnolang/gno/tm2/pkg/crypto/keys" ) -type importCfg struct { - rootCfg *baseCfg +type ImportCfg struct { + RootCfg *BaseCfg - keyName string - armorPath string - unsafe bool + KeyName string + ArmorPath string + Unsafe bool } -func newImportCmd(rootCfg *baseCfg, io commands.IO) *commands.Command { - cfg := &importCfg{ - rootCfg: rootCfg, +func NewImportCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command { + cfg := &ImportCfg{ + RootCfg: rootCfg, } return commands.NewCommand( @@ -37,50 +37,51 @@ func newImportCmd(rootCfg *baseCfg, io commands.IO) *commands.Command { ) } -func (c *importCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *ImportCfg) RegisterFlags(fs *flag.FlagSet) { fs.StringVar( - &c.keyName, + &c.KeyName, "name", "", "The name of the private key", ) fs.StringVar( - &c.armorPath, + &c.ArmorPath, "armor-path", "", "The path to the encrypted armor file", ) fs.BoolVar( - &c.unsafe, + &c.Unsafe, "unsafe", false, "Import the private key armor as unencrypted", ) } -func execImport(cfg *importCfg, io commands.IO) error { +func execImport(cfg *ImportCfg, io commands.IO) error { // check keyname - if cfg.keyName == "" { + if cfg.KeyName == "" { return errors.New("name shouldn't be empty") } + // Create a new instance of the key-base - kb, err := keys.NewKeyBaseFromDir(cfg.rootCfg.Home) + kb, err := keys.NewKeyBaseFromDir(cfg.RootCfg.Home) if err != nil { return fmt.Errorf( "unable to create a key base from directory %s, %w", - cfg.rootCfg.Home, + cfg.RootCfg.Home, err, ) } // Read the raw encrypted armor - armor, err := os.ReadFile(cfg.armorPath) + armor, err := os.ReadFile(cfg.ArmorPath) if err != nil { return fmt.Errorf( "unable to read armor from path %s, %w", - cfg.armorPath, + cfg.ArmorPath, err, ) } @@ -90,11 +91,11 @@ func execImport(cfg *importCfg, io commands.IO) error { encryptPassword string ) - if !cfg.unsafe { + if !cfg.Unsafe { // Get the armor decrypt password decryptPassword, err = io.GetPassword( "Enter the passphrase to decrypt your private key armor:", - cfg.rootCfg.InsecurePasswordStdin, + cfg.RootCfg.InsecurePasswordStdin, ) if err != nil { return fmt.Errorf( @@ -110,7 +111,7 @@ func execImport(cfg *importCfg, io commands.IO) error { "Enter a passphrase to encrypt your private key:", "Repeat the passphrase:", }, - cfg.rootCfg.InsecurePasswordStdin, + cfg.RootCfg.InsecurePasswordStdin, ) if err != nil { return fmt.Errorf( @@ -119,10 +120,10 @@ func execImport(cfg *importCfg, io commands.IO) error { ) } - if cfg.unsafe { + if cfg.Unsafe { // Import the unencrypted private key if err := kb.ImportPrivKeyUnsafe( - cfg.keyName, + cfg.KeyName, string(armor), encryptPassword, ); err != nil { @@ -134,7 +135,7 @@ func execImport(cfg *importCfg, io commands.IO) error { } else { // Import the encrypted private key if err := kb.ImportPrivKey( - cfg.keyName, + cfg.KeyName, string(armor), decryptPassword, encryptPassword, @@ -146,7 +147,7 @@ func execImport(cfg *importCfg, io commands.IO) error { } } - io.Printfln("Successfully imported private key %s", cfg.keyName) + io.Printfln("Successfully imported private key %s", cfg.KeyName) return nil } diff --git a/tm2/pkg/crypto/keys/client/import_test.go b/tm2/pkg/crypto/keys/client/import_test.go index 9324817d9ac..9788f3d700c 100644 --- a/tm2/pkg/crypto/keys/client/import_test.go +++ b/tm2/pkg/crypto/keys/client/import_test.go @@ -22,16 +22,16 @@ func importKey( importOpts testImportKeyOpts, input io.Reader, ) error { - cfg := &importCfg{ - rootCfg: &baseCfg{ + cfg := &ImportCfg{ + RootCfg: &BaseCfg{ BaseOptions: BaseOptions{ Home: importOpts.kbHome, InsecurePasswordStdin: true, }, }, - keyName: importOpts.keyName, - armorPath: importOpts.armorPath, - unsafe: importOpts.unsafe, + KeyName: importOpts.keyName, + ArmorPath: importOpts.armorPath, + Unsafe: importOpts.unsafe, } cmdIO := commands.NewTestIO() diff --git a/tm2/pkg/crypto/keys/client/list.go b/tm2/pkg/crypto/keys/client/list.go index bdee6b3bbe9..5a2c0dfb28a 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, io commands.IO) *commands.Command { +func NewListCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command { return commands.NewCommand( commands.Metadata{ Name: "list", @@ -22,7 +22,7 @@ func newListCmd(rootCfg *baseCfg, io commands.IO) *commands.Command { ) } -func execList(cfg *baseCfg, args []string, io commands.IO) error { +func execList(cfg *BaseCfg, args []string, io commands.IO) error { if len(args) != 0 { return flag.ErrHelp } diff --git a/tm2/pkg/crypto/keys/client/list_test.go b/tm2/pkg/crypto/keys/client/list_test.go index ee0a147f3d3..69e96f1cfb1 100644 --- a/tm2/pkg/crypto/keys/client/list_test.go +++ b/tm2/pkg/crypto/keys/client/list_test.go @@ -36,7 +36,7 @@ func Test_execList(t *testing.T) { for _, tt := range testData { t.Run(tt.name, func(t *testing.T) { // Set current home - cfg := &baseCfg{ + cfg := &BaseCfg{ BaseOptions: BaseOptions{ Home: tt.kbDir, }, diff --git a/tm2/pkg/crypto/keys/client/maketx.go b/tm2/pkg/crypto/keys/client/maketx.go index c424b566c95..c78c5de25c8 100644 --- a/tm2/pkg/crypto/keys/client/maketx.go +++ b/tm2/pkg/crypto/keys/client/maketx.go @@ -2,24 +2,30 @@ package client import ( "flag" + "fmt" + "github.com/gnolang/gno/tm2/pkg/amino" + types "github.com/gnolang/gno/tm2/pkg/bft/rpc/core/types" "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/crypto/keys" + "github.com/gnolang/gno/tm2/pkg/errors" + "github.com/gnolang/gno/tm2/pkg/std" ) -type makeTxCfg struct { - rootCfg *baseCfg +type MakeTxCfg struct { + RootCfg *BaseCfg - gasWanted int64 - gasFee string - memo string + GasWanted int64 + GasFee string + Memo string - broadcast bool - chainID string + Broadcast bool + ChainID string } -func newMakeTxCmd(rootCfg *baseCfg, io commands.IO) *commands.Command { - cfg := &makeTxCfg{ - rootCfg: rootCfg, +func NewMakeTxCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command { + cfg := &MakeTxCfg{ + RootCfg: rootCfg, } cmd := commands.NewCommand( @@ -33,48 +39,148 @@ func newMakeTxCmd(rootCfg *baseCfg, io commands.IO) *commands.Command { ) cmd.AddSubCommands( - newAddPkgCmd(cfg, io), - newSendCmd(cfg, io), - newCallCmd(cfg, io), - newRunCmd(cfg, io), + NewMakeSendCmd(cfg, io), ) return cmd } -func (c *makeTxCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *MakeTxCfg) RegisterFlags(fs *flag.FlagSet) { fs.Int64Var( - &c.gasWanted, + &c.GasWanted, "gas-wanted", 0, "gas requested for tx", ) fs.StringVar( - &c.gasFee, + &c.GasFee, "gas-fee", "", "gas payment fee", ) fs.StringVar( - &c.memo, + &c.Memo, "memo", "", "any descriptive text", ) fs.BoolVar( - &c.broadcast, + &c.Broadcast, "broadcast", false, "sign and broadcast", ) fs.StringVar( - &c.chainID, + &c.ChainID, "chainid", "dev", "chainid to sign for (only useful if --broadcast)", ) } + +func SignAndBroadcastHandler( + cfg *MakeTxCfg, + nameOrBech32 string, + tx std.Tx, + pass string, +) (*types.ResultBroadcastTxCommit, error) { + baseopts := cfg.RootCfg + txopts := cfg + + kb, err := keys.NewKeyBaseFromDir(cfg.RootCfg.Home) + if err != nil { + return nil, err + } + + info, err := kb.GetByNameOrAddress(nameOrBech32) + if err != nil { + return nil, err + } + accountAddr := info.GetAddress() + + qopts := &QueryCfg{ + RootCfg: baseopts, + Path: fmt.Sprintf("auth/accounts/%s", accountAddr), + } + qres, err := QueryHandler(qopts) + if err != nil { + return nil, errors.Wrap(err, "query account") + } + var qret struct{ BaseAccount std.BaseAccount } + err = amino.UnmarshalJSON(qres.Response.Data, &qret) + if err != nil { + return nil, err + } + + // sign tx + accountNumber := qret.BaseAccount.AccountNumber + sequence := qret.BaseAccount.Sequence + sopts := &SignCfg{ + Pass: pass, + RootCfg: baseopts, + Sequence: sequence, + AccountNumber: accountNumber, + ChainID: txopts.ChainID, + NameOrBech32: nameOrBech32, + TxJSON: amino.MustMarshalJSON(tx), + } + + signedTx, err := SignHandler(sopts) + if err != nil { + return nil, errors.Wrap(err, "sign tx") + } + + // broadcast signed tx + bopts := &BroadcastCfg{ + RootCfg: baseopts, + tx: signedTx, + } + + return BroadcastHandler(bopts) +} + +func ExecSignAndBroadcast( + cfg *MakeTxCfg, + args []string, + tx std.Tx, + io commands.IO, +) error { + baseopts := cfg.RootCfg + + // query account + nameOrBech32 := args[0] + + var err error + var pass string + if baseopts.Quiet { + pass, err = io.GetPassword("", baseopts.InsecurePasswordStdin) + } else { + pass, err = io.GetPassword("Enter password.", baseopts.InsecurePasswordStdin) + } + + if err != nil { + return err + } + + bres, err := SignAndBroadcastHandler(cfg, nameOrBech32, tx, pass) + if err != nil { + return errors.Wrap(err, "broadcast tx") + } + if bres.CheckTx.IsErr() { + return errors.Wrap(bres.CheckTx.Error, "check transaction failed: log:%s", bres.CheckTx.Log) + } + if bres.DeliverTx.IsErr() { + return errors.Wrap(bres.DeliverTx.Error, "deliver transaction failed: log:%s", bres.DeliverTx.Log) + } + + io.Println(string(bres.DeliverTx.Data)) + io.Println("OK!") + io.Println("GAS WANTED:", bres.DeliverTx.GasWanted) + io.Println("GAS USED: ", bres.DeliverTx.GasUsed) + + return nil +} diff --git a/tm2/pkg/crypto/keys/client/query.go b/tm2/pkg/crypto/keys/client/query.go index 746048e772c..4c37a125749 100644 --- a/tm2/pkg/crypto/keys/client/query.go +++ b/tm2/pkg/crypto/keys/client/query.go @@ -10,20 +10,19 @@ import ( "github.com/gnolang/gno/tm2/pkg/errors" ) -type queryCfg struct { - rootCfg *baseCfg +type QueryCfg struct { + RootCfg *BaseCfg - data string - height int64 - prove bool + Data string + Height int64 + Prove bool - // internal - path string + Path string } -func newQueryCmd(rootCfg *baseCfg, io commands.IO) *commands.Command { - cfg := &queryCfg{ - rootCfg: rootCfg, +func NewQueryCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command { + cfg := &QueryCfg{ + RootCfg: rootCfg, } return commands.NewCommand( @@ -39,37 +38,37 @@ func newQueryCmd(rootCfg *baseCfg, io commands.IO) *commands.Command { ) } -func (c *queryCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *QueryCfg) RegisterFlags(fs *flag.FlagSet) { fs.StringVar( - &c.data, + &c.Data, "data", "", "query data bytes", ) fs.Int64Var( - &c.height, + &c.Height, "height", 0, "query height (not yet supported)", ) fs.BoolVar( - &c.prove, + &c.Prove, "prove", false, "prove query result (not yet supported)", ) } -func execQuery(cfg *queryCfg, args []string, io commands.IO) error { +func execQuery(cfg *QueryCfg, args []string, io commands.IO) error { if len(args) != 1 { return flag.ErrHelp } - cfg.path = args[0] + cfg.Path = args[0] - qres, err := queryHandler(cfg) + qres, err := QueryHandler(cfg) if err != nil { return err } @@ -90,20 +89,20 @@ func execQuery(cfg *queryCfg, args []string, io commands.IO) error { return nil } -func queryHandler(cfg *queryCfg) (*ctypes.ResultABCIQuery, error) { - remote := cfg.rootCfg.Remote +func QueryHandler(cfg *QueryCfg) (*ctypes.ResultABCIQuery, error) { + remote := cfg.RootCfg.Remote if remote == "" || remote == "y" { return nil, errors.New("missing remote url") } - data := []byte(cfg.data) + data := []byte(cfg.Data) opts2 := client.ABCIQueryOptions{ // Height: height, XXX // Prove: false, XXX } cli := client.NewHTTP(remote, "/websocket") qres, err := cli.ABCIQueryWithOptions( - cfg.path, data, opts2) + cfg.Path, data, opts2) if err != nil { return nil, errors.Wrap(err, "querying") } diff --git a/tm2/pkg/crypto/keys/client/root.go b/tm2/pkg/crypto/keys/client/root.go index e09b31d45dd..bfccdc26bab 100644 --- a/tm2/pkg/crypto/keys/client/root.go +++ b/tm2/pkg/crypto/keys/client/root.go @@ -14,7 +14,7 @@ const ( mnemonicEntropySize = 256 ) -type baseCfg struct { +type BaseCfg struct { BaseOptions } @@ -23,7 +23,7 @@ func NewRootCmd(io commands.IO) *commands.Command { } func NewRootCmdWithBaseConfig(io commands.IO, base BaseOptions) *commands.Command { - cfg := &baseCfg{ + cfg := &BaseCfg{ BaseOptions: base, } @@ -41,23 +41,23 @@ func NewRootCmdWithBaseConfig(io commands.IO, base BaseOptions) *commands.Comman ) cmd.AddSubCommands( - 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), + 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 } -func (c *baseCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *BaseCfg) RegisterFlags(fs *flag.FlagSet) { // Base options fs.StringVar( &c.Home, diff --git a/tm2/pkg/crypto/keys/client/send.go b/tm2/pkg/crypto/keys/client/send.go index a5098aea08c..a253f9d9f4e 100644 --- a/tm2/pkg/crypto/keys/client/send.go +++ b/tm2/pkg/crypto/keys/client/send.go @@ -14,16 +14,16 @@ import ( "github.com/gnolang/gno/tm2/pkg/std" ) -type sendCfg struct { - rootCfg *makeTxCfg +type MakeSendCfg struct { + RootCfg *MakeTxCfg - send string - to string + Send string + To string } -func newSendCmd(rootCfg *makeTxCfg, io commands.IO) *commands.Command { - cfg := &sendCfg{ - rootCfg: rootCfg, +func NewMakeSendCmd(rootCfg *MakeTxCfg, io commands.IO) *commands.Command { + cfg := &MakeSendCfg{ + RootCfg: rootCfg, } return commands.NewCommand( @@ -34,48 +34,48 @@ func newSendCmd(rootCfg *makeTxCfg, io commands.IO) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execSend(cfg, args, io) + return execMakeSend(cfg, args, io) }, ) } -func (c *sendCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *MakeSendCfg) RegisterFlags(fs *flag.FlagSet) { fs.StringVar( - &c.send, + &c.Send, "send", "", "send amount", ) fs.StringVar( - &c.to, + &c.To, "to", "", "destination address", ) } -func execSend(cfg *sendCfg, args []string, io commands.IO) error { +func execMakeSend(cfg *MakeSendCfg, args []string, io commands.IO) error { if len(args) != 1 { return flag.ErrHelp } - if cfg.rootCfg.gasWanted == 0 { + if cfg.RootCfg.GasWanted == 0 { return errors.New("gas-wanted not specified") } - if cfg.rootCfg.gasFee == "" { + if cfg.RootCfg.GasFee == "" { return errors.New("gas-fee not specified") } - if cfg.send == "" { + if cfg.Send == "" { return errors.New("send (amount) must be specified") } - if cfg.to == "" { + if cfg.To == "" { return errors.New("to (destination address) must be specified") } // read account pubkey. nameOrBech32 := args[0] - kb, err := keys.NewKeyBaseFromDir(cfg.rootCfg.rootCfg.Home) + kb, err := keys.NewKeyBaseFromDir(cfg.RootCfg.RootCfg.Home) if err != nil { return err } @@ -87,20 +87,20 @@ func execSend(cfg *sendCfg, args []string, io commands.IO) error { // info.GetPubKey() // Parse to address. - toAddr, err := crypto.AddressFromBech32(cfg.to) + toAddr, err := crypto.AddressFromBech32(cfg.To) if err != nil { return err } // Parse send amount. - send, err := std.ParseCoins(cfg.send) + send, err := std.ParseCoins(cfg.Send) if err != nil { return errors.Wrap(err, "parsing send coins") } // parse gas wanted & fee. - gaswanted := cfg.rootCfg.gasWanted - gasfee, err := std.ParseCoin(cfg.rootCfg.gasFee) + gaswanted := cfg.RootCfg.GasWanted + gasfee, err := std.ParseCoin(cfg.RootCfg.GasFee) if err != nil { return errors.Wrap(err, "parsing gas fee coin") } @@ -115,11 +115,11 @@ func execSend(cfg *sendCfg, args []string, io commands.IO) error { Msgs: []std.Msg{msg}, Fee: std.NewFee(gaswanted, gasfee), Signatures: nil, - Memo: cfg.rootCfg.memo, + Memo: cfg.RootCfg.Memo, } - if cfg.rootCfg.broadcast { - err := signAndBroadcast(cfg.rootCfg, args, tx, io) + if cfg.RootCfg.Broadcast { + err := ExecSignAndBroadcast(cfg.RootCfg, args, tx, io) if err != nil { return err } diff --git a/tm2/pkg/crypto/keys/client/sign.go b/tm2/pkg/crypto/keys/client/sign.go index f8fcc02fdde..19022eaba81 100644 --- a/tm2/pkg/crypto/keys/client/sign.go +++ b/tm2/pkg/crypto/keys/client/sign.go @@ -2,7 +2,6 @@ package client import ( "context" - "errors" "flag" "fmt" "os" @@ -10,27 +9,26 @@ import ( "github.com/gnolang/gno/tm2/pkg/amino" "github.com/gnolang/gno/tm2/pkg/commands" "github.com/gnolang/gno/tm2/pkg/crypto/keys" + "github.com/gnolang/gno/tm2/pkg/errors" "github.com/gnolang/gno/tm2/pkg/std" ) -type signCfg struct { - rootCfg *baseCfg - - txPath string - chainID string - accountNumber uint64 - sequence uint64 - showSignBytes bool - - // internal flags, when called programmatically - nameOrBech32 string - txJSON []byte - pass string +type SignCfg struct { + RootCfg *BaseCfg + + TxPath string + ChainID string + AccountNumber uint64 + Sequence uint64 + ShowSignBytes bool + NameOrBech32 string + TxJSON []byte + Pass string } -func newSignCmd(rootCfg *baseCfg, io commands.IO) *commands.Command { - cfg := &signCfg{ - rootCfg: rootCfg, +func NewSignCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command { + cfg := &SignCfg{ + RootCfg: rootCfg, } return commands.NewCommand( @@ -46,54 +44,54 @@ func newSignCmd(rootCfg *baseCfg, io commands.IO) *commands.Command { ) } -func (c *signCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *SignCfg) RegisterFlags(fs *flag.FlagSet) { fs.StringVar( - &c.txPath, + &c.TxPath, "txpath", "-", "path to file of tx to sign", ) fs.StringVar( - &c.chainID, + &c.ChainID, "chainid", "dev", "chainid to sign for", ) fs.Uint64Var( - &c.accountNumber, + &c.AccountNumber, "number", 0, "account number to sign with (required)", ) fs.Uint64Var( - &c.sequence, + &c.Sequence, "sequence", 0, "sequence to sign with (required)", ) fs.BoolVar( - &c.showSignBytes, + &c.ShowSignBytes, "show-signbytes", false, "show sign bytes and quit", ) } -func execSign(cfg *signCfg, args []string, io commands.IO) error { +func execSign(cfg *SignCfg, args []string, io commands.IO) error { var err error if len(args) != 1 { return flag.ErrHelp } - cfg.nameOrBech32 = args[0] + cfg.NameOrBech32 = args[0] // read tx to sign - txpath := cfg.txPath + txpath := cfg.TxPath if txpath == "-" { // from stdin. txjsonstr, err := io.GetString( "Enter tx to sign, terminated by a newline.", @@ -101,23 +99,23 @@ func execSign(cfg *signCfg, args []string, io commands.IO) error { if err != nil { return err } - cfg.txJSON = []byte(txjsonstr) + cfg.TxJSON = []byte(txjsonstr) } else { // from file - cfg.txJSON, err = os.ReadFile(txpath) + cfg.TxJSON, err = os.ReadFile(txpath) if err != nil { return err } } - if cfg.rootCfg.Quiet { - cfg.pass, err = io.GetPassword( + if cfg.RootCfg.Quiet { + cfg.Pass, err = io.GetPassword( "", - cfg.rootCfg.InsecurePasswordStdin, + cfg.RootCfg.InsecurePasswordStdin, ) } else { - cfg.pass, err = io.GetPassword( + cfg.Pass, err = io.GetPassword( "Enter password.", - cfg.rootCfg.InsecurePasswordStdin, + cfg.RootCfg.InsecurePasswordStdin, ) } if err != nil { @@ -138,20 +136,20 @@ func execSign(cfg *signCfg, args []string, io commands.IO) error { return nil } -func SignHandler(cfg *signCfg) (*std.Tx, error) { +func SignHandler(cfg *SignCfg) (*std.Tx, error) { var err error var tx std.Tx - if cfg.txJSON == nil { + if cfg.TxJSON == nil { return nil, errors.New("invalid tx content") } - kb, err := keys.NewKeyBaseFromDir(cfg.rootCfg.Home) + kb, err := keys.NewKeyBaseFromDir(cfg.RootCfg.Home) if err != nil { return nil, err } - err = amino.UnmarshalJSON(cfg.txJSON, &tx) + err = amino.UnmarshalJSON(cfg.TxJSON, &tx) if err != nil { return nil, err } @@ -174,16 +172,16 @@ func SignHandler(cfg *signCfg) (*std.Tx, error) { } // derive sign doc bytes. - chainID := cfg.chainID - accountNumber := cfg.accountNumber - sequence := cfg.sequence + chainID := cfg.ChainID + accountNumber := cfg.AccountNumber + sequence := cfg.Sequence signbz := tx.GetSignBytes(chainID, accountNumber, sequence) - if cfg.showSignBytes { + if cfg.ShowSignBytes { fmt.Printf("sign bytes: %X\n", signbz) return nil, nil } - sig, pub, err := kb.Sign(cfg.nameOrBech32, cfg.pass, signbz) + sig, pub, err := kb.Sign(cfg.NameOrBech32, cfg.Pass, signbz) if err != nil { return nil, err } @@ -201,7 +199,7 @@ func SignHandler(cfg *signCfg) (*std.Tx, error) { } if !found { return nil, errors.New( - fmt.Sprintf("addr %v (%s) not in signer set", addr, cfg.nameOrBech32), + fmt.Sprintf("addr %v (%s) not in signer set", addr, cfg.NameOrBech32), ) } diff --git a/tm2/pkg/crypto/keys/client/sign_test.go b/tm2/pkg/crypto/keys/client/sign_test.go index 6e9b1da5946..0d73d247637 100644 --- a/tm2/pkg/crypto/keys/client/sign_test.go +++ b/tm2/pkg/crypto/keys/client/sign_test.go @@ -23,17 +23,17 @@ func Test_execSign(t *testing.T) { defer kbCleanUp() // initialize test options - cfg := &signCfg{ - rootCfg: &baseCfg{ + cfg := &SignCfg{ + RootCfg: &BaseCfg{ BaseOptions: BaseOptions{ Home: kbHome, InsecurePasswordStdin: true, }, }, - txPath: "-", // stdin - chainID: "dev", - accountNumber: 0, - sequence: 0, + TxPath: "-", // stdin + ChainID: "dev", + AccountNumber: 0, + Sequence: 0, } fakeKeyName1 := "signApp_Key1" @@ -43,7 +43,7 @@ func Test_execSign(t *testing.T) { io := commands.NewTestIO() // add test account to keybase. - kb, err := keys.NewKeyBaseFromDir(cfg.rootCfg.Home) + kb, err := keys.NewKeyBaseFromDir(cfg.RootCfg.Home) assert.NoError(t, err) acc, err := kb.CreateAccount(fakeKeyName1, testMnemonic, "", encPassword, 0, 0) addr := acc.GetAddress() diff --git a/tm2/pkg/crypto/keys/client/verify.go b/tm2/pkg/crypto/keys/client/verify.go index fff2fcd852f..5a52ba76a3c 100644 --- a/tm2/pkg/crypto/keys/client/verify.go +++ b/tm2/pkg/crypto/keys/client/verify.go @@ -10,15 +10,15 @@ import ( "github.com/gnolang/gno/tm2/pkg/crypto/keys" ) -type verifyCfg struct { - rootCfg *baseCfg +type VerifyCfg struct { + RootCfg *BaseCfg - docPath string + DocPath string } -func newVerifyCmd(rootCfg *baseCfg, io commands.IO) *commands.Command { - cfg := &verifyCfg{ - rootCfg: rootCfg, +func NewVerifyCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command { + cfg := &VerifyCfg{ + RootCfg: rootCfg, } return commands.NewCommand( @@ -34,16 +34,16 @@ func newVerifyCmd(rootCfg *baseCfg, io commands.IO) *commands.Command { ) } -func (c *verifyCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *VerifyCfg) RegisterFlags(fs *flag.FlagSet) { fs.StringVar( - &c.docPath, + &c.DocPath, "docpath", "", "path of document file to verify", ) } -func execVerify(cfg *verifyCfg, args []string, io commands.IO) error { +func execVerify(cfg *VerifyCfg, args []string, io commands.IO) error { var ( kb keys.Keybase err error @@ -58,8 +58,8 @@ func execVerify(cfg *verifyCfg, args []string, io commands.IO) error { if err != nil { return err } - docpath := cfg.docPath - kb, err = keys.NewKeyBaseFromDir(cfg.rootCfg.Home) + docpath := cfg.DocPath + kb, err = keys.NewKeyBaseFromDir(cfg.RootCfg.Home) if err != nil { return err } diff --git a/tm2/pkg/crypto/keys/client/verify_test.go b/tm2/pkg/crypto/keys/client/verify_test.go index 206c14682fd..796f6344852 100644 --- a/tm2/pkg/crypto/keys/client/verify_test.go +++ b/tm2/pkg/crypto/keys/client/verify_test.go @@ -21,14 +21,14 @@ func Test_execVerify(t *testing.T) { defer kbCleanUp() // initialize test options - cfg := &verifyCfg{ - rootCfg: &baseCfg{ + cfg := &VerifyCfg{ + RootCfg: &BaseCfg{ BaseOptions: BaseOptions{ Home: kbHome, InsecurePasswordStdin: true, }, }, - docPath: "", + DocPath: "", } io := commands.NewTestIO()