From 1a909be6a02751bba377debe898f39d89af45c06 Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Fri, 14 Jun 2019 13:10:07 -0400 Subject: [PATCH] Fixed bad handling of core symbol The core symbol is now retrieved on-demand when a command requires it. The current retrieval way is to query the `rammarket` table which should have a row only when `eosio:init` has been called with the core symbol set. This row has a quote balance asset value that is always equalt to the core symbol. This is where we get it. In the eventuality that it's not possible to retrieve, the user can still override the default 4,EOS by passing `--core-symbol` to the `eosc` CLI. This will use this core symbol only when impossible to infer it. Also added logging facilities to help debugging certain part of the `eosc` CLI. Fixes #136 --- eosc/cmd/common.go | 93 +++++++++++++++++++++++++++------- eosc/cmd/logging.go | 11 ++++ eosc/cmd/rexBuy.go | 2 +- eosc/cmd/rexDefundCPU.go | 2 +- eosc/cmd/rexDefundNET.go | 2 +- eosc/cmd/rexDeposit.go | 2 +- eosc/cmd/rexFundCPU.go | 2 +- eosc/cmd/rexFundNet.go | 2 +- eosc/cmd/rexRentCPU.go | 4 +- eosc/cmd/rexRentNet.go | 4 +- eosc/cmd/rexUnstakeTo.go | 4 +- eosc/cmd/rexWithdraw.go | 2 +- eosc/cmd/root.go | 10 ++++ eosc/cmd/systemBidname.go | 3 +- eosc/cmd/systemDelegateBW.go | 6 +-- eosc/cmd/systemNewaccount.go | 9 ++-- eosc/cmd/systemUndelegateBW.go | 10 ++-- eosc/cmd/toolsSellAccount.go | 3 +- eosc/cmd/transfer.go | 8 ++- go.mod | 4 +- go.sum | 2 + 21 files changed, 125 insertions(+), 60 deletions(-) create mode 100644 eosc/cmd/logging.go diff --git a/eosc/cmd/common.go b/eosc/cmd/common.go index fb80e9f9..68773877 100644 --- a/eosc/cmd/common.go +++ b/eosc/cmd/common.go @@ -11,6 +11,8 @@ import ( "strings" "time" + "go.uber.org/zap" + yaml2json "github.com/bronze1man/go-yaml2json" "github.com/eoscanada/eos-go" "github.com/eoscanada/eos-go/ecc" @@ -18,6 +20,7 @@ import ( "github.com/eoscanada/eosc/cli" eosvault "github.com/eoscanada/eosc/vault" "github.com/spf13/viper" + "github.com/tidwall/gjson" "github.com/tidwall/sjson" ) @@ -85,6 +88,58 @@ func getAPI() *eos.API { return api } +var coreSymbolIsCached bool +var coreSymbol eos.Symbol + +func getCoreSymbol() eos.Symbol { + if coreSymbolIsCached { + return coreSymbol + } + + // In the event of a failure, we do not want to re-perform an API call, + // so let's record the fact that getCoreSymbol is cached right here. + // The init core symbol will take care of setting an approriate core + // symbol from global flag and reporting the error. + coreSymbolIsCached = true + if err := initCoreSymbol(); err != nil { + coreSymbol = eos.EOSSymbol + zlog.Debug( + "unable to retrieve core symbol from API, falling back to default", + zap.Error(err), + zap.Stringer("default", coreSymbol), + ) + } + + return coreSymbol +} + +func initCoreSymbol() error { + resp, err := getAPI().GetTableRows(eos.GetTableRowsRequest{ + Code: "eosio", + Scope: "eosio", + Table: "rammarket", + JSON: true, + }) + + if err != nil { + return fmt.Errorf("unable to fetch table: %s", err) + } + + result := gjson.GetBytes(resp.Rows, "0.quote.balance") + if !result.Exists() { + return errors.New("table has not expected format") + } + + asset, err := eos.NewAsset(result.String()) + if !result.Exists() { + return fmt.Errorf("quote balance asset %q is not valid: %s", result.String(), err) + } + + zlog.Debug("Retrieved core symbol from API, using it as default core symbol", zap.Stringer("symbol", asset.Symbol)) + coreSymbol = asset.Symbol + return nil +} + func sanitizeAPIURL(input string) string { return strings.TrimRight(input, "/") } @@ -312,32 +367,39 @@ func loadYAMLOrJSONFile(filename string, v interface{}) error { func toAccount(in, field string) eos.AccountName { acct, err := cli.ToAccountName(in) - if err != nil { - errorCheck(fmt.Sprintf("invalid account format for %q", field), err) - } + errorCheck(fmt.Sprintf("invalid account format for %q", field), err) return acct } func toAsset(symbol eos.Symbol, in, field string) eos.Asset { - asset, err := eos.NewAssetFromString(symbol, in) - errorCheck(fmt.Sprintf("invalid %s asset for %q", symbol.String(), field), err) + asset, err := eos.NewFixedSymbolAssetFromString(symbol, in) + errorCheck(fmt.Sprintf("invalid %q value %q", field, in), err) return asset } -func toEOSAsset(in, field string) eos.Asset { - asset, err := eos.NewEOSAssetFromString(in) - errorCheck(fmt.Sprintf("invalid %s asset for %q", eos.EOSSymbol, field), err) +func toAssetWithDefaultCoreSymbol(in, field string) eos.Asset { + if len(strings.Split(in, " ")) == 1 { + return toCoreAsset(in, field) + } + + asset, err := eos.NewAssetFromString(in) + errorCheck(fmt.Sprintf("invalid asset value %q for %q", in, field), err) return asset } -func toREXAsset(in, field string) eos.Asset { - asset, err := eos.NewREXAssetFromString(in) - errorCheck(fmt.Sprintf("invalid %s asset for %q", eos.REXSymbol, field), err) +func toCoreAsset(in, field string) eos.Asset { + return toAsset(getCoreSymbol(), in, field) +} - return asset +func toEOSAsset(in, field string) eos.Asset { + return toAsset(eos.EOSSymbol, in, field) +} + +func toREXAsset(in, field string) eos.Asset { + return toAsset(eos.REXSymbol, in, field) } func toName(in, field string) eos.Name { @@ -395,10 +457,3 @@ func isStubABI(abi eos.ABI) bool { abi.Structs == nil && abi.Tables == nil && abi.Types == nil } - -func NewAssetDefaultEOS(input string) (eos.Asset, error) { - if len(strings.Split(input, " ")) == 1 { - return eos.NewEOSAssetFromString(input) - } - return eos.NewAsset(input) -} diff --git a/eosc/cmd/logging.go b/eosc/cmd/logging.go new file mode 100644 index 00000000..c6ecae81 --- /dev/null +++ b/eosc/cmd/logging.go @@ -0,0 +1,11 @@ +package cmd + +import ( + "go.uber.org/zap" +) + +var zlog = zap.NewNop() + +func SetLogger(l *zap.Logger) { + zlog = l +} diff --git a/eosc/cmd/rexBuy.go b/eosc/cmd/rexBuy.go index 29c6ee02..1129b393 100644 --- a/eosc/cmd/rexBuy.go +++ b/eosc/cmd/rexBuy.go @@ -14,7 +14,7 @@ var rexBuy = &cobra.Command{ Args: cobra.ExactArgs(2), Run: func(cmd *cobra.Command, args []string) { account := toAccount(args[0], "account") - quantity := toEOSAsset(args[1], "quantity") + quantity := toCoreAsset(args[1], "quantity") pushEOSCActions(getAPI(), rex.NewBuyREX( account, diff --git a/eosc/cmd/rexDefundCPU.go b/eosc/cmd/rexDefundCPU.go index 7dd701a7..c0341e90 100644 --- a/eosc/cmd/rexDefundCPU.go +++ b/eosc/cmd/rexDefundCPU.go @@ -15,7 +15,7 @@ var rexDefundCPU = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { account := toAccount(args[0], "account") loanNumber := toUint64(args[1], "loan number") - quantity := toEOSAsset(args[2], "quantity") + quantity := toCoreAsset(args[2], "quantity") pushEOSCActions(getAPI(), rex.NewDefundCPULoan( account, diff --git a/eosc/cmd/rexDefundNET.go b/eosc/cmd/rexDefundNET.go index 01fb3913..4c99b3c4 100644 --- a/eosc/cmd/rexDefundNET.go +++ b/eosc/cmd/rexDefundNET.go @@ -15,7 +15,7 @@ var rexDefundNet = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { account := toAccount(args[0], "account") loanNumber := toUint64(args[1], "loan number") - quantity := toEOSAsset(args[2], "quantity") + quantity := toCoreAsset(args[2], "quantity") pushEOSCActions(getAPI(), rex.NewDefundNetLoan( account, diff --git a/eosc/cmd/rexDeposit.go b/eosc/cmd/rexDeposit.go index a5891b80..0b4ad031 100644 --- a/eosc/cmd/rexDeposit.go +++ b/eosc/cmd/rexDeposit.go @@ -14,7 +14,7 @@ var rexDeposit = &cobra.Command{ Args: cobra.ExactArgs(2), Run: func(cmd *cobra.Command, args []string) { account := toAccount(args[0], "account") - quantity := toEOSAsset(args[1], "quantity") + quantity := toCoreAsset(args[1], "quantity") pushEOSCActions(getAPI(), rex.NewDeposit( account, diff --git a/eosc/cmd/rexFundCPU.go b/eosc/cmd/rexFundCPU.go index d919ecc3..2dec9f78 100644 --- a/eosc/cmd/rexFundCPU.go +++ b/eosc/cmd/rexFundCPU.go @@ -15,7 +15,7 @@ var rexFundCPU = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { account := toAccount(args[0], "account") loanNumber := toUint64(args[1], "loan number") - quantity := toEOSAsset(args[2], "quantity") + quantity := toCoreAsset(args[2], "quantity") pushEOSCActions(getAPI(), rex.NewFundCPULoan( account, diff --git a/eosc/cmd/rexFundNet.go b/eosc/cmd/rexFundNet.go index 58919bbd..8d713544 100644 --- a/eosc/cmd/rexFundNet.go +++ b/eosc/cmd/rexFundNet.go @@ -15,7 +15,7 @@ var rexFundNet = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { account := toAccount(args[0], "account") loanNumber := toUint64(args[1], "loan number") - quantity := toEOSAsset(args[2], "quantity") + quantity := toCoreAsset(args[2], "quantity") pushEOSCActions(getAPI(), rex.NewFundNetLoan( account, diff --git a/eosc/cmd/rexRentCPU.go b/eosc/cmd/rexRentCPU.go index 8432eebe..502546bb 100644 --- a/eosc/cmd/rexRentCPU.go +++ b/eosc/cmd/rexRentCPU.go @@ -15,8 +15,8 @@ var rexRentCPU = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { payer := toAccount(args[0], "payer") receiver := toAccount(args[1], "receiver") - quantity := toEOSAsset(args[2], "quantity") - loanFund := toEOSAsset(args[3], "loan fund") + quantity := toCoreAsset(args[2], "quantity") + loanFund := toCoreAsset(args[3], "loan fund") pushEOSCActions(getAPI(), rex.NewRentCPU( payer, diff --git a/eosc/cmd/rexRentNet.go b/eosc/cmd/rexRentNet.go index 120f7975..b6a0f0be 100644 --- a/eosc/cmd/rexRentNet.go +++ b/eosc/cmd/rexRentNet.go @@ -15,8 +15,8 @@ var rexRentNet = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { payer := toAccount(args[0], "payer") receiver := toAccount(args[1], "receiver") - quantity := toEOSAsset(args[2], "quantity") - loanFund := toEOSAsset(args[3], "loan fund") + quantity := toCoreAsset(args[2], "quantity") + loanFund := toCoreAsset(args[3], "loan fund") pushEOSCActions(getAPI(), rex.NewRentNet( payer, diff --git a/eosc/cmd/rexUnstakeTo.go b/eosc/cmd/rexUnstakeTo.go index 04565097..d227d480 100644 --- a/eosc/cmd/rexUnstakeTo.go +++ b/eosc/cmd/rexUnstakeTo.go @@ -15,8 +15,8 @@ var rexUnstakeTo = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { staker := toAccount(args[0], "staker") stakedTo := toAccount(args[1], "staked to") - net := toEOSAsset(args[2], "net") - cpu := toEOSAsset(args[3], "cpu") + net := toCoreAsset(args[2], "net") + cpu := toCoreAsset(args[3], "cpu") pushEOSCActions(getAPI(), rex.NewUnstakeToREX( staker, diff --git a/eosc/cmd/rexWithdraw.go b/eosc/cmd/rexWithdraw.go index b1789fdf..64d210e2 100644 --- a/eosc/cmd/rexWithdraw.go +++ b/eosc/cmd/rexWithdraw.go @@ -14,7 +14,7 @@ var rexWithdraw = &cobra.Command{ Args: cobra.ExactArgs(2), Run: func(cmd *cobra.Command, args []string) { account := toAccount(args[0], "account") - quantity := toEOSAsset(args[1], "quantity") + quantity := toCoreAsset(args[1], "quantity") pushEOSCActions(getAPI(), rex.NewWithdraw( account, diff --git a/eosc/cmd/root.go b/eosc/cmd/root.go index aafdcc40..f28b2978 100644 --- a/eosc/cmd/root.go +++ b/eosc/cmd/root.go @@ -5,6 +5,8 @@ import ( "os" "strings" + "go.uber.org/zap" + "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/spf13/viper" @@ -41,6 +43,7 @@ func Execute() { func init() { cobra.OnInitialize(initConfig) + RootCmd.PersistentFlags().StringP("core-symbol", "c", "", "Core symbol to use for all commands (default inferred from API if possible, 4,EOS otherwise)") RootCmd.PersistentFlags().BoolP("debug", "", false, "Enables verbose API debug messages") RootCmd.PersistentFlags().StringP("vault-file", "", "./eosc-vault.json", "Wallet file that contains encrypted key material") RootCmd.PersistentFlags().StringSliceP("wallet-url", "", []string{}, "Base URL to wallet endpoint. You can pass this multiple times to use the multi-signer (will use each wallet to sign multi-sig transactions).") @@ -65,6 +68,13 @@ func initConfig() { viper.SetEnvKeyReplacer(replacer) recurseViperCommands(RootCmd, nil) + + if viper.GetBool("global-debug") { + zlog, err := zap.NewDevelopment() + if err == nil { + SetLogger(zlog) + } + } } func recurseViperCommands(root *cobra.Command, segments []string) { diff --git a/eosc/cmd/systemBidname.go b/eosc/cmd/systemBidname.go index 1f8635a4..b2ea5d43 100644 --- a/eosc/cmd/systemBidname.go +++ b/eosc/cmd/systemBidname.go @@ -28,8 +28,7 @@ Read https://steemit.com/eos/@eos-canada/everything-you-need-to-know-about-names bidder := toAccount(args[0], "bidder_account_name") newname := toAccount(args[1], "premium_account_name") - bidAsset, err := NewAssetDefaultEOS(args[2]) - errorCheck("bid amount invalid", err) + bidAsset := toCoreAsset(args[2], "bid quantity") fmt.Printf("[%s] bidding for: %s , amount=%d precision=%d symbol=%s\n", bidder, newname, bidAsset.Amount, bidAsset.Symbol.Precision, bidAsset.Symbol.Symbol) diff --git a/eosc/cmd/systemDelegateBW.go b/eosc/cmd/systemDelegateBW.go index e5301276..d26f75bf 100644 --- a/eosc/cmd/systemDelegateBW.go +++ b/eosc/cmd/systemDelegateBW.go @@ -45,10 +45,8 @@ Alternatively, you can use the simplified: Run: func(cmd *cobra.Command, args []string) { from := toAccount(args[0], "from") receiver := toAccount(args[1], "receiver") - netStake, err := NewAssetDefaultEOS(args[2]) - errorCheck(`"network bw stake qty" invalid`, err) - cpuStake, err := NewAssetDefaultEOS(args[3]) - errorCheck(`"cpu bw stake qty" invalid`, err) + netStake := toCoreAsset(args[2], "network bw stake qty") + cpuStake := toCoreAsset(args[3], "cpu bw stake qty") transfer := viper.GetBool("system-delegatebw-cmd-transfer") api := getAPI() diff --git a/eosc/cmd/systemNewaccount.go b/eosc/cmd/systemNewaccount.go index 3cdfe77d..d6dbf073 100644 --- a/eosc/cmd/systemNewaccount.go +++ b/eosc/cmd/systemNewaccount.go @@ -104,10 +104,8 @@ active: errorCheck("missing argument", fmt.Errorf("--stake-net missing")) } - cpuStake, err := NewAssetDefaultEOS(cpuStakeStr) - errorCheck("--stake-cpu invalid", err) - netStake, err := NewAssetDefaultEOS(netStakeStr) - errorCheck("--stake-net invalid", err) + cpuStake := toCoreAsset(cpuStakeStr, "--stake-cpu") + netStake := toCoreAsset(netStakeStr, "--stake-net") doTransfer := viper.GetBool("system-newaccount-cmd-transfer") if cpuStake.Amount != 0 || netStake.Amount != 0 { @@ -118,8 +116,7 @@ active: buyRAM := viper.GetString("system-newaccount-cmd-buy-ram") if buyRAM != "" { - buyRAMAmount, err := NewAssetDefaultEOS(buyRAM) - errorCheck("--buy-ram invalid", err) + buyRAMAmount := toCoreAsset(buyRAM, "--buy-ram") actions = append(actions, system.NewBuyRAM(creator, newAccount, uint64(buyRAMAmount.Amount))) } else { diff --git a/eosc/cmd/systemUndelegateBW.go b/eosc/cmd/systemUndelegateBW.go index dfd2bc38..cbcdcf09 100644 --- a/eosc/cmd/systemUndelegateBW.go +++ b/eosc/cmd/systemUndelegateBW.go @@ -21,14 +21,10 @@ See also: the "system delegatebw" command. Run: func(cmd *cobra.Command, args []string) { from := toAccount(args[0], "from") receiver := toAccount(args[1], "receiver") - netStake, err := NewAssetDefaultEOS(args[2]) - errorCheck(`"network bw unstake qty" invalid`, err) - cpuStake, err := NewAssetDefaultEOS(args[3]) - errorCheck(`"cpu bw unstake qty" invalid`, err) + netStake := toCoreAsset(args[2], "network bw unstake qty") + cpuStake := toCoreAsset(args[3], "cpu bw unstake qty") - api := getAPI() - - pushEOSCActions(api, system.NewUndelegateBW(from, receiver, cpuStake, netStake)) + pushEOSCActions(getAPI(), system.NewUndelegateBW(from, receiver, cpuStake, netStake)) }, } diff --git a/eosc/cmd/toolsSellAccount.go b/eosc/cmd/toolsSellAccount.go index 53f59266..a5c33027 100644 --- a/eosc/cmd/toolsSellAccount.go +++ b/eosc/cmd/toolsSellAccount.go @@ -28,8 +28,7 @@ MAKE SURE TO INSPECT THE GENERATED MULTISIG TRANSACTION BEFORE APPROVING IT. soldAccount := toAccount(args[0], "sold account") buyerAccount := toAccount(args[1], "buyer account") beneficiaryAccount := toAccount(args[2], "beneficiary account") - saleAmount, err := NewAssetDefaultEOS(args[3]) - errorCheck(`sale "amount" invalid`, err) + saleAmount := toCoreAsset(args[3], "amount") proposalName := viper.GetString("tools-sell-account-cmd-proposal-name") memo := viper.GetString("tools-sell-account-cmd-memo") diff --git a/eosc/cmd/transfer.go b/eosc/cmd/transfer.go index f9bcffd8..24d7d8ae 100644 --- a/eosc/cmd/transfer.go +++ b/eosc/cmd/transfer.go @@ -17,15 +17,13 @@ var transferCmd = &cobra.Command{ from := toAccount(args[0], "from") to := toAccount(args[1], "to") - quantity, err := NewAssetDefaultEOS(args[2]) - errorCheck("invalid amount", err) + quantity := toAssetWithDefaultCoreSymbol(args[2], "quantity") memo := viper.GetString("transfer-cmd-memo") - api := getAPI() - action := token.NewTransfer(from, to, quantity, memo) action.Account = contract - pushEOSCActions(api, action) + + pushEOSCActions(getAPI(), action) }, } diff --git a/go.mod b/go.mod index 1d9d2e77..bd1560a6 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/abourget/llerrgroup v0.0.0-20161118145731-75f536392d17 github.com/bronze1man/go-yaml2json v0.0.0-20150129175009-f6f64b738964 github.com/davecgh/go-spew v1.1.1 - github.com/eoscanada/eos-go v0.8.13 + github.com/eoscanada/eos-go v0.8.14-0.20190614154328-0014a42badb5 github.com/golang/protobuf v1.3.1 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/mailru/easyjson v0.0.0-20190403194419-1ea4449da983 // indirect @@ -25,7 +25,7 @@ require ( github.com/tidwall/sjson v1.0.0 go.uber.org/atomic v1.3.2 // indirect go.uber.org/multierr v1.1.0 // indirect - go.uber.org/zap v1.9.1 // indirect + go.uber.org/zap v1.9.1 golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613 golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e // indirect golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890 diff --git a/go.sum b/go.sum index 3283af15..9c5ce24f 100644 --- a/go.sum +++ b/go.sum @@ -18,6 +18,8 @@ github.com/eoscanada/eos-bios v1.2.0 h1:h2PC3rbOCvPVkq6+RapwUGKn0wZ41ScJmHHTwdDK github.com/eoscanada/eos-bios v1.2.0/go.mod h1:64+2vEnagr6kY1QgIexkeOXPEXn4yeLDiluCzQ2B9dY= github.com/eoscanada/eos-go v0.8.13 h1:2U6a+cj+VygmcrKP8Y2op1qukynXUTfhyhKrysjPmsY= github.com/eoscanada/eos-go v0.8.13/go.mod h1:RKrm2XzZEZWxSMTRqH5QOyJ1fb/qKEjs2ix1aQl0sk4= +github.com/eoscanada/eos-go v0.8.14-0.20190614154328-0014a42badb5 h1:6Y6xlTLOaY7Uzb/aarC4hpqkztamhpyX8ELGXFRre00= +github.com/eoscanada/eos-go v0.8.14-0.20190614154328-0014a42badb5/go.mod h1:RKrm2XzZEZWxSMTRqH5QOyJ1fb/qKEjs2ix1aQl0sk4= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=