From 1ac07ed63aefb0c0b70eefdb8f1ca0ada68232a5 Mon Sep 17 00:00:00 2001 From: Leonid Edrenkin Date: Tue, 24 Sep 2024 22:43:36 +0200 Subject: [PATCH] add `address` subcommand to encode/decode address to/from puzzle hash (#2) * add wallet encode/decode commands --------- Co-authored-by: Cameron Cooper --- .github/workflows/release.yml | 2 +- go.mod | 5 ++- go.sum | 17 +++++--- internal/cmd/coinset/address.go | 20 ++++++++++ internal/cmd/coinset/address_decode.go | 41 +++++++++++++++++++ internal/cmd/coinset/address_encode.go | 55 ++++++++++++++++++++++++++ internal/cmd/coinset/root.go | 6 +-- internal/cmd/coinset/util.go | 47 ++++++++++++---------- 8 files changed, 163 insertions(+), 30 deletions(-) create mode 100644 internal/cmd/coinset/address.go create mode 100644 internal/cmd/coinset/address_decode.go create mode 100644 internal/cmd/coinset/address_encode.go diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f1cb728..4e44430 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -82,7 +82,7 @@ jobs: uses: goreleaser/goreleaser-action@v5 with: distribution: goreleaser - version: latest + version: ~> v1 args: release --clean env: GITHUB_TOKEN: ${{ secrets.ACTIONS_GITHUB_TOKEN }} \ No newline at end of file diff --git a/go.mod b/go.mod index 3e81ae0..8fc4798 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,10 @@ module github.com/coinset-org/cli -go 1.17 +go 1.21 require ( github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 + github.com/chia-network/go-chia-libs v0.15.0 github.com/itchyny/gojq v0.12.9 github.com/spf13/cobra v1.6.1 ) @@ -15,6 +16,8 @@ require ( github.com/itchyny/timefmt-go v0.1.4 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.16 // indirect + github.com/samber/mo v1.13.0 // indirect github.com/spf13/pflag v1.0.5 // indirect golang.org/x/sys v0.1.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 251c341..d4ebbc9 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,12 @@ github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 h1:ZBbLwSJqkHBuFDA6DUhhse0IGJ7T5bemHyNILUjvOq4= github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2/go.mod h1:VSw57q4QFiWDbRnjdX8Cb3Ow0SFncRw+bA/ofY6Q83w= +github.com/chia-network/go-chia-libs v0.15.0 h1:auNhv6azbMyWgIXrfysRtQDteoR+/cOPCxHRwjukn+g= +github.com/chia-network/go-chia-libs v0.15.0/go.mod h1:npTqaFSjTdMxE7hc0LOmWJmWGqcs+IERarK5fDxXk/I= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f h1:7LYC+Yfkj3CTRcShK0KOL/w6iTiKyqqBA9a41Wnggw8= github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f/go.mod h1:pFlLw2CfqZiIBOx6BuCeRLCrfxBJipTY0nIOF/VbGcI= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= @@ -19,20 +22,24 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/samber/mo v1.13.0 h1:LB1OwfJMju3a6FjghH+AIvzMG0ZPOzgTWj1qaHs1IQ4= +github.com/samber/mo v1.13.0/go.mod h1:BfkrCPuYzVG3ZljnZB783WIJIGk1mcZr9c9CPf8tAxs= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/cmd/coinset/address.go b/internal/cmd/coinset/address.go new file mode 100644 index 0000000..ad19646 --- /dev/null +++ b/internal/cmd/coinset/address.go @@ -0,0 +1,20 @@ +package cmd + +import "github.com/spf13/cobra" + +var addressCmd = &cobra.Command{ + Use: "address", + Short: "Encode/decode address to/from puzzle hash", + Long: `Encode/decode address to/from puzzle hash.`, +} + +func init() { + addressCmd.SetHelpFunc(func(command *cobra.Command, strings []string) { + command.Flags().MarkHidden("api") + command.Flags().MarkHidden("mainnet") + command.Flags().MarkHidden("raw") + command.Flags().MarkHidden("query") + addressCmd.Parent().HelpFunc()(command, strings) + }) + rootCmd.AddCommand(addressCmd) +} diff --git a/internal/cmd/coinset/address_decode.go b/internal/cmd/coinset/address_decode.go new file mode 100644 index 0000000..6e3c707 --- /dev/null +++ b/internal/cmd/coinset/address_decode.go @@ -0,0 +1,41 @@ +package cmd + +import ( + "fmt" + + "github.com/chia-network/go-chia-libs/pkg/bech32m" + "github.com/spf13/cobra" +) + +func init() { + addressDecodeCmd.SetHelpFunc(func(command *cobra.Command, strings []string) { + command.Flags().MarkHidden("testnet") + command.Parent().HelpFunc()(command, strings) + }) + + addressCmd.AddCommand(addressDecodeCmd) +} + +var addressDecodeCmd = &cobra.Command{ + Use: "decode
", + Args: func(cmd *cobra.Command, args []string) error { + if err := cobra.ExactArgs(1)(cmd, args); err != nil { + return err + } + if isAddress(args[0]) { + return nil + } + return fmt.Errorf("invalid address value specified: %s", args[0]) + }, + Short: "Decode address to puzzle hash", + Long: `Decode address to puzzle hash`, + Run: func(cmd *cobra.Command, args []string) { + _, puzzleHashBytes, err := bech32m.DecodePuzzleHash(args[0]) + if err != nil { + fmt.Println(err) + return + } + + fmt.Println(puzzleHashBytes.String()) + }, +} diff --git a/internal/cmd/coinset/address_encode.go b/internal/cmd/coinset/address_encode.go new file mode 100644 index 0000000..89e250f --- /dev/null +++ b/internal/cmd/coinset/address_encode.go @@ -0,0 +1,55 @@ +package cmd + +import ( + "encoding/hex" + "fmt" + + "github.com/chia-network/go-chia-libs/pkg/bech32m" + "github.com/chia-network/go-chia-libs/pkg/types" + "github.com/spf13/cobra" +) + +func init() { + addressCmd.AddCommand(addressEncodeCmd) +} + +var addressEncodeCmd = &cobra.Command{ + Use: "encode ", + Args: func(cmd *cobra.Command, args []string) error { + if err := cobra.ExactArgs(1)(cmd, args); err != nil { + return err + } + if isHex(args[0]) { + return nil + } + return fmt.Errorf("invalid hex value specified: %s", args[0]) + }, + Short: "Encode puzzle hash to address", + Long: `Encode puzzle hash to address`, + Run: func(cmd *cobra.Command, args []string) { + prefix := "xch" + if testnet { + prefix = "txch" + } + var puzzleHash = formatHex(args[0]) + hexBytes, err := hex.DecodeString(puzzleHash[2:]) + if err != nil { + fmt.Println(err) + return + } + + hexBytes32, err := types.BytesToBytes32(hexBytes) + if err != nil { + fmt.Println(err) + return + } + + address, err := bech32m.EncodePuzzleHash(hexBytes32, prefix) + if err != nil { + fmt.Println(err) + return + } + + fmt.Println(address) + }, +} diff --git a/internal/cmd/coinset/root.go b/internal/cmd/coinset/root.go index 02d7010..e4ef78a 100644 --- a/internal/cmd/coinset/root.go +++ b/internal/cmd/coinset/root.go @@ -1,8 +1,8 @@ package cmd import ( + "net/http" "os" - "net/http" "github.com/spf13/cobra" ) @@ -12,7 +12,7 @@ var client = &http.Client{} var rootCmd = &cobra.Command{ Use: "coinset", Short: "Make Chia RPC requests", - Long: `Coinset is a hosted Chia API. Use this CLI to make requests to it.`, + Long: `Coinset is a hosted Chia API. Use this CLI to make requests to it.`, } func Execute() { @@ -40,7 +40,7 @@ var version = "dev" func init() { rootCmd.PersistentFlags().StringVarP(&jq, "query", "q", ".", "filter to apply using jq syntax") rootCmd.PersistentFlags().BoolVar(&mainnet, "mainnet", false, "Use mainnet as the network") - rootCmd.PersistentFlags().BoolVar(&testnet, "testnet", false, "Use the latest testnet as the network") + rootCmd.PersistentFlags().BoolVarP(&testnet, "testnet", "t", false, "Use the latest testnet as the network") rootCmd.PersistentFlags().StringVarP(&api, "api", "a", "", "api host to use") rootCmd.PersistentFlags().BoolVarP(&raw, "raw", "r", false, "display output in raw json") rootCmd.MarkFlagsMutuallyExclusive("mainnet", "testnet", "api") diff --git a/internal/cmd/coinset/util.go b/internal/cmd/coinset/util.go index bb65a09..2610702 100644 --- a/internal/cmd/coinset/util.go +++ b/internal/cmd/coinset/util.go @@ -12,14 +12,19 @@ import ( "github.com/itchyny/gojq" ) +func isAddress(str string) bool { + r, err := regexp.MatchString("^(xch|txch){1}[0-9A-Za-z]{59}$", str) + return err == nil && r +} + func isHex(str string) bool { r, err := regexp.MatchString("^(0x)?[0-9A-Fa-f]+$", str) - return err == nil && r == true + return err == nil && r } func formatHex(str string) string { r, _ := regexp.MatchString("^0x[0-9A-Fa-f]+$", str) - if r == true { + if r { return str } return "0x" + str @@ -29,8 +34,8 @@ func apiRoot() string { if api != "" { return api } - if testnet == true { - return "https://testnet10.coinset.org" + if testnet { + return "https://testnet11.api.coinset.org" } return "https://api.coinset.org" } @@ -54,21 +59,21 @@ func makeRequest(rpc string, jsonData map[string]interface{}) { return } - var result map[string]interface{} - byteResult, _ := io.ReadAll(resp.Body) - - if raw { - fmt.Println(string(byteResult)) + jsonBytes, err := io.ReadAll(resp.Body) + if err != nil { + fmt.Println(err) return } - json.Unmarshal(byteResult, &result) - query, err := gojq.Parse(jq) if err != nil { fmt.Println(err) } - iter := query.Run(result) // or query.RunWithContext + + var jsonStrings map[string]interface{} + json.Unmarshal(jsonBytes, &jsonStrings) + + iter := query.Run(jsonStrings) for { v, ok := iter.Next() if !ok { @@ -78,14 +83,16 @@ func makeRequest(rpc string, jsonData map[string]interface{}) { fmt.Println(err) } - f := colorjson.NewFormatter() - f.Indent = 2 - - s, _ := f.Marshal(v) - fmt.Println(string(s)) - } -} + if raw { + s, _ := json.Marshal(v) + fmt.Println(string(s)) + } else { + f := colorjson.NewFormatter() + f.Indent = 2 -func handleRequest(req *http.Request, err error) { + s, _ := f.Marshal(v) + fmt.Println(string(s)) + } + } }