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()