diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 5008ddfc..00000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.gitignore b/.gitignore index 57b573a1..84dbb414 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -.idea -routers/commentsRouter_controllers.go -gotron -*.tmp -vendor/** \ No newline at end of file +tronctl +tonctl-docs +bin +.idea/ +*.key diff --git a/.gitmodules b/.gitmodules index 5a690582..c7c3e76e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,4 @@ path = protocol url = https://github.com/tronprotocol/protocol.git branch = master + ignore = dirty \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..008a1a47 --- /dev/null +++ b/Makefile @@ -0,0 +1,30 @@ +SHELL := /bin/bash +version := $(shell git rev-list --count HEAD) +commit := $(shell git describe --always --long --dirty) +built_at := $(shell date +%FT%T%z) +built_by := ${USER}@cryptochain.network +BUILD_TARGET := tronctl + +flags := -gcflags="all=-N -l -c 2" +ldflags := -X main.version=v${version} -X main.commit=${commit} +ldflags += -X main.builtAt=${built_at} -X main.builtBy=${built_by} +cli := ./bin/${BUILD_TARGET} +uname := $(shell uname) + +env := GO111MODULE=on + +DIR := ${CURDIR} +export CGO_LDFLAGS=-L$(DIR)/bin/lib -Wl,-rpath -Wl,\$ORIGIN/lib + +all: + $(env) go build -o $(cli) -ldflags="$(ldflags)" cmd/main.go + +debug: + $(env) go build $(flags) -o $(cli) -ldflags="$(ldflags)" cmd/main.go + +install:all + cp $(cli) ~/.local/bin + +clean: + @rm -f $(cli) + @rm -rf ./bin \ No newline at end of file diff --git a/README.md b/README.md index 1d4c9d29..4d134593 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,63 @@ -## gotron ![](https://img.shields.io/badge/progress-80%25-yellow.svg) +# TRON's go-sdk +GoSDK and TRON-CLI tool for TRON's blockchain via GRPC -Fork from [sasaxie/go-client-api](https://github.com/sasaxie/go-client-api) +# Build -## Requirements +``` +$ git pull -r origin master +$ make +``` -- Go 1.13 or higher +# Usage & Examples -## Installation +# bash completions -First you need to install ProtocolBuffers 3.0.0-beta-3 or later. +once built, add `tronctl` to your path and add to your `.bashrc` -```sh -mkdir tmp -cd tmp -git clone https://github.com/google/protobuf -cd protobuf -./autogen.sh -./configure -make -make check -sudo make install +``` +. <(tronctl completion) ``` -Then, `go get -u` as usual the following packages: - -```sh -go get -u github.com/golang/protobuf/protoc-gen-go +## Transfer JSON file format +The JSON file will be a JSON array where each element has the following attributes: + +| Key | Value-type | Value-description| +| :------------------:|:----------:| :----------------| +| `from` | string | [**Required**] Sender's one address, must have key in keystore. | +| `to` | string | [**Required**] The receivers one address. | +| `amount` | string | [**Required**] The amount to send in $ONE. | +| `passphrase-file` | string | [*Optional*] The file path to file containing the passphrase in plain text. If none is provided, check for passphrase string. | +| `passphrase-string` | string | [*Optional*] The passphrase as a string in plain text. If none is provided, passphrase is ''. | +| `stop-on-error` | boolean | [*Optional*] If true, stop sending transactions if an error occurred, default is false. | + +Example of JSON file: + +```json +[ + { + "from": "TUEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH", + "to": "TKSXDA8HfE9E1y39RczVQ1ZascUEtaSToF", + "amount": "1", + "passphrase-string": "", + "stop-on-error": true + }, + { + "from": "TUEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH", + "to": "TEvHMZWyfjCAdDJEKYxYVL8rRpigddLC1R", + "amount": "1", + "passphrase-file": "./pw.txt", + } +] ``` -Update protocol: -```sh -git submodule update --remote -``` +# Debugging -Example: +The gotron-sdk code respects `GOTRON_SDK_DEBUG` as debugging +based environment variables. -```sh -go get -u github.com/fbsobreira/gotron -go run program/getnowblock.go -grpcAddress grpc.trongrid.io:50051 +```bash +GOTRON_SDK_DEBUG=true ./tronctl ``` diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 00000000..5e3bb5be --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,39 @@ +package main + +import ( + "fmt" + "os" + "path" + + cmd "github.com/fbsobreira/gotron-sdk/cmd/subcommands" + // Need this side effect + _ "github.com/fbsobreira/gotron-sdk/pkg/store" + "github.com/spf13/cobra" +) + +var ( + version string + commit string + builtAt string + builtBy string +) + +func main() { + // HACK Force usage of go implementation rather than the C based one. Do the right way, see the + // notes one line 66,67 of https://golang.org/src/net/net.go that say can make the decision at + // build time. + os.Setenv("GODEBUG", "netdns=go") + cmd.VersionWrapDump = version + "-" + commit + cmd.RootCmd.AddCommand(&cobra.Command{ + Use: "version", + Short: "Show version", + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Fprintf(os.Stderr, + "TronCTL. %v version %v-%v (%v %v)\n", + path.Base(os.Args[0]), version, commit, builtBy, builtAt) + os.Exit(0) + return nil + }, + }) + cmd.Execute() +} diff --git a/cmd/subcommands/account.go b/cmd/subcommands/account.go new file mode 100644 index 00000000..b7c725c3 --- /dev/null +++ b/cmd/subcommands/account.go @@ -0,0 +1,108 @@ +package cmd + +import ( + "encoding/json" + "fmt" + + "github.com/fbsobreira/gotron-sdk/pkg/client/transaction" + "github.com/fbsobreira/gotron-sdk/pkg/common" + "github.com/fbsobreira/gotron-sdk/pkg/keystore" + "github.com/fbsobreira/gotron-sdk/pkg/store" + "github.com/spf13/cobra" +) + +var ( + balanceDetails bool +) + +func accountSub() []*cobra.Command { + cmdBalance := &cobra.Command{ + Use: "balance ", + Short: "Check account balance", + Long: "Query for the latest account balance given Address", + Args: cobra.ExactArgs(1), + PreRunE: validateAddress, + RunE: func(cmd *cobra.Command, args []string) error { + acc, err := conn.GetAccount(addr.String()) + if err != nil { + return err + } + + if noPrettyOutput { + fmt.Println(acc) + return nil + } + + result := make(map[string]interface{}) + result["address"] = addr.String() + result["type"] = acc.GetType() + result["name"] = acc.GetAccountName() + result["balance"] = float64(acc.GetBalance()) / 1000000 + asJSON, _ := json.Marshal(result) + fmt.Println(common.JSONPrettyFormat(string(asJSON))) + return nil + }, + } + cmdBalance.Flags().BoolVar(&balanceDetails, "details", false, "") + + cmdActivate := &cobra.Command{ + Use: "activate ", + Short: "activate an address", + Args: cobra.ExactArgs(1), + PreRunE: validateAddress, + RunE: func(cmd *cobra.Command, args []string) error { + if signerAddress.String() == "" { + return fmt.Errorf("no signer specified") + } + tx, err := conn.CreateAccount(signerAddress.String(), addr.String()) + if err != nil { + return err + } + + var ctrlr *transaction.Controller + if useLedgerWallet { + account := keystore.Account{Address: signerAddress.GetAddress()} + ctrlr = transaction.NewController(conn, nil, &account, tx.Transaction, opts) + } else { + ks, acct, err := store.UnlockedKeystore(signerAddress.String(), passphrase) + if err != nil { + return err + } + ctrlr = transaction.NewController(conn, ks, acct, tx.Transaction, opts) + } + if err = ctrlr.ExecuteTransaction(0); err != nil { + return err + } + + if noPrettyOutput { + fmt.Println(tx) + return nil + } + + result := make(map[string]interface{}) + result["receipt"] = map[string]interface{}{ + "message": string(ctrlr.Receipt.Message), + } + + asJSON, _ := json.Marshal(result) + fmt.Println(common.JSONPrettyFormat(string(asJSON))) + return nil + }, + } + + return []*cobra.Command{cmdBalance, cmdActivate} +} + +func init() { + cmdAccount := &cobra.Command{ + Use: "account", + Short: "Check account balance", + RunE: func(cmd *cobra.Command, args []string) error { + cmd.Help() + return nil + }, + } + + cmdAccount.AddCommand(accountSub()...) + RootCmd.AddCommand(cmdAccount) +} diff --git a/cmd/subcommands/assets.go b/cmd/subcommands/assets.go new file mode 100644 index 00000000..f29271a9 --- /dev/null +++ b/cmd/subcommands/assets.go @@ -0,0 +1,55 @@ +package cmd + +import ( + "encoding/json" + "fmt" + + "github.com/fbsobreira/gotron-sdk/pkg/common" + "github.com/spf13/cobra" +) + +func assetsSub() []*cobra.Command { + cmdIssue := &cobra.Command{ + Use: "issue ", + Short: "Check account balance", + Long: "Query for the latest account balance given Address", + Args: cobra.ExactArgs(1), + PreRunE: validateAddress, + RunE: func(cmd *cobra.Command, args []string) error { + acc, err := conn.GetAccount(addr.String()) + if err != nil { + return err + } + + if noPrettyOutput { + fmt.Println(acc) + return nil + } + + result := make(map[string]interface{}) + result["address"] = addr.String() + result["type"] = acc.GetType() + result["name"] = acc.GetAccountName() + result["balance"] = float64(acc.GetBalance()) / 1000000 + asJSON, _ := json.Marshal(result) + fmt.Println(common.JSONPrettyFormat(string(asJSON))) + return nil + }, + } + + return []*cobra.Command{cmdIssue} +} + +func init() { + cmdAssets := &cobra.Command{ + Use: "assets", + Short: "Assets Manager", + RunE: func(cmd *cobra.Command, args []string) error { + cmd.Help() + return nil + }, + } + + cmdAssets.AddCommand(assetsSub()...) + RootCmd.AddCommand(cmdAssets) +} diff --git a/cmd/subcommands/completion.go b/cmd/subcommands/completion.go new file mode 100644 index 00000000..b3a8d8e3 --- /dev/null +++ b/cmd/subcommands/completion.go @@ -0,0 +1,25 @@ +package cmd + +import ( + "os" + + "github.com/spf13/cobra" +) + +func init() { + cmdCompletion := &cobra.Command{ + Use: "completion", + Short: "Generates bash completion scripts", + Long: `To load completion, run: + + . <(tronctl completion) + +Add the line to your ~/.bashrc to enable completion for each bash session. +`, + RunE: func(cmd *cobra.Command, args []string) error { + RootCmd.GenBashCompletion(os.Stdout) + return nil + }, + } + RootCmd.AddCommand(cmdCompletion) +} diff --git a/cmd/subcommands/custom-flags.go b/cmd/subcommands/custom-flags.go new file mode 100644 index 00000000..ce85f70f --- /dev/null +++ b/cmd/subcommands/custom-flags.go @@ -0,0 +1,35 @@ +package cmd + +import ( + "github.com/fbsobreira/gotron-sdk/pkg/address" + "github.com/pkg/errors" +) + +type tronAddress struct { + address string +} + +func (tronAddress tronAddress) String() string { + return tronAddress.address +} + +func (tronAddress *tronAddress) Set(s string) error { + _, err := address.Base58ToAddress(s) + if err != nil { + return errors.Wrap(err, "not a valid one address") + } + tronAddress.address = s + return nil +} + +func (tronAddress *tronAddress) GetAddress() address.Address { + addr, err := address.Base58ToAddress(tronAddress.address) + if err != nil { + return address.Address{} + } + return addr +} + +func (tronAddress tronAddress) Type() string { + return "tron-address" +} diff --git a/cmd/subcommands/keys.go b/cmd/subcommands/keys.go new file mode 100644 index 00000000..a8ba99f6 --- /dev/null +++ b/cmd/subcommands/keys.go @@ -0,0 +1,326 @@ +package cmd + +import ( + "bufio" + "errors" + "fmt" + "io/ioutil" + "os" + "strings" + + "github.com/fatih/color" + "github.com/fbsobreira/gotron-sdk/pkg/account" + c "github.com/fbsobreira/gotron-sdk/pkg/common" + + "github.com/fbsobreira/gotron-sdk/pkg/ledger" + "github.com/fbsobreira/gotron-sdk/pkg/mnemonic" + "github.com/fbsobreira/gotron-sdk/pkg/store" + "github.com/spf13/cobra" + "github.com/tyler-smith/go-bip39" + "golang.org/x/crypto/ssh/terminal" +) + +const ( + seedPhraseWarning = "**Important** write this seed phrase in a safe place, " + + "it is the only way to recover your account if you ever forget your password\n\n" +) + +var ( + quietImport bool + recoverFromMnemonic bool + userProvidesPassphrase bool + passphraseFilePath string + passphrase string + blsFilePath string + blsShardID uint32 + blsCount uint32 + ppPrompt = fmt.Sprintf( + "prompt for passphrase, otherwise use default passphrase: \"`%s`\"", c.DefaultPassphrase, + ) +) + +// getPassphrase fetches the correct passphrase depending on if a file is available to +// read from or if the user wants to enter in their own passphrase. Otherwise, just use +// the default passphrase. No confirmation of passphrase +func getPassphrase() (string, error) { + if passphraseFilePath != "" { + if _, err := os.Stat(passphraseFilePath); os.IsNotExist(err) { + return "", fmt.Errorf("passphrase file not found at `%s`", passphraseFilePath) + } + dat, err := ioutil.ReadFile(passphraseFilePath) + if err != nil { + return "", err + } + pw := strings.TrimSuffix(string(dat), "\n") + return pw, nil + } else if userProvidesPassphrase { + fmt.Println("Enter passphrase:") + pass, err := terminal.ReadPassword(int(os.Stdin.Fd())) + if err != nil { + return "", err + } + return string(pass), nil + } else { + return c.DefaultPassphrase, nil + } +} + +// getPassphrase fetches the correct passphrase depending on if a file is available to +// read from or if the user wants to enter in their own passphrase. Otherwise, just use +// the default passphrase. Passphrase requires a confirmation +func getPassphraseWithConfirm() (string, error) { + if passphraseFilePath != "" { + if _, err := os.Stat(passphraseFilePath); os.IsNotExist(err) { + return "", fmt.Errorf("passphrase file not found at `%s`", passphraseFilePath) + } + dat, err := ioutil.ReadFile(passphraseFilePath) + if err != nil { + return "", err + } + pw := strings.TrimSuffix(string(dat), "\n") + return pw, nil + } else if userProvidesPassphrase { + fmt.Println("Enter passphrase:") + pass, err := terminal.ReadPassword(int(os.Stdin.Fd())) + if err != nil { + return "", err + } + fmt.Println("Repeat the passphrase:") + repeatPass, err := terminal.ReadPassword(int(os.Stdin.Fd())) + if err != nil { + return "", err + } + if string(repeatPass) != string(pass) { + return "", errors.New("passphrase does not match") + } + fmt.Println("") // provide feedback when passphrase is entered. + return string(repeatPass), nil + } else { + return c.DefaultPassphrase, nil + } +} + +func keysSub() []*cobra.Command { + cmdList := &cobra.Command{ + Use: "list", + Short: "List all the local accounts", + RunE: func(cmd *cobra.Command, args []string) error { + if useLedgerWallet { + ledger.ProcessAddressCommand() + return nil + } + store.DescribeLocalAccounts() + return nil + }, + } + + cmdLocation := &cobra.Command{ + Use: "location", + Short: "Show where `tronctl` keeps accounts & their keys", + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Println(store.DefaultLocation()) + return nil + }, + } + + cmdAdd := &cobra.Command{ + Use: "add ", + Short: "Create a new keystore key", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + if store.DoesNamedAccountExist(args[0]) { + return fmt.Errorf("account %s already exists", args[0]) + } + passphrase, err := getPassphraseWithConfirm() + if err != nil { + return err + } + acc := account.Creation{ + Name: args[0], + Passphrase: passphrase, + } + + if err := account.CreateNewLocalAccount(&acc); err != nil { + return err + } + if !recoverFromMnemonic { + color.Red(seedPhraseWarning) + fmt.Println(acc.Mnemonic) + } + addr, _ := store.AddressFromAccountName(acc.Name) + fmt.Printf("Tron Address: %s\n", addr) + return nil + }, + } + cmdAdd.Flags().BoolVar(&userProvidesPassphrase, "passphrase", false, ppPrompt) + cmdAdd.Flags().StringVar(&passphraseFilePath, "passphrase-file", "", "path to a file containing the passphrase") + + cmdRemove := &cobra.Command{ + Use: "remove ", + Short: "Remove a key from the keystore", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + if err := account.RemoveAccount(args[0]); err != nil { + return err + } + return nil + }, + } + + cmdMnemonic := &cobra.Command{ + Use: "mnemonic", + Short: "Compute the bip39 mnemonic for some input entropy", + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Println(mnemonic.Generate()) + return nil + }, + } + + cmdRecoverMnemonic := &cobra.Command{ + Use: "recover-from-mnemonic [ACCOUNT_NAME]", + Short: "Recover account from mnemonic", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + if store.DoesNamedAccountExist(args[0]) { + return fmt.Errorf("account %s already exists", args[0]) + } + passphrase, err := getPassphraseWithConfirm() + if err != nil { + return err + } + acc := account.Creation{ + Name: args[0], + Passphrase: passphrase, + } + fmt.Println("Enter mnemonic to recover keys from") + scanner := bufio.NewScanner(os.Stdin) + scanner.Scan() + m := scanner.Text() + if !bip39.IsMnemonicValid(m) { + return fmt.Errorf("invalid mnemonic given") + } + + fmt.Println("Enter mnemonic password [optional]") + scanner.Scan() + p := scanner.Text() + + acc.Mnemonic = m + acc.MnemonicPassphrase = p + + if err := account.CreateNewLocalAccount(&acc); err != nil { + return err + } + fmt.Println("Successfully recovered account from mnemonic!") + addr, _ := store.AddressFromAccountName(acc.Name) + fmt.Printf("Tron Address: %s\n", addr) + return nil + }, + } + cmdRecoverMnemonic.Flags().BoolVar(&userProvidesPassphrase, "passphrase", false, ppPrompt) + cmdRecoverMnemonic.Flags().StringVar(&passphraseFilePath, "passphrase-file", "", "path to a file containing the passphrase") + + cmdImportKS := &cobra.Command{ + Use: "import-ks [ACCOUNT_NAME]", + Args: cobra.RangeArgs(1, 2), + Short: "Import an existing keystore key", + RunE: func(cmd *cobra.Command, args []string) error { + userName := "" + if len(args) == 2 { + userName = args[1] + } + passphrase, err := getPassphrase() + if err != nil { + return err + } + name, err := account.ImportKeyStore(args[0], userName, passphrase) + if !quietImport && err == nil { + fmt.Printf("Imported keystore given account alias of `%s`\n", name) + addr, _ := store.AddressFromAccountName(name) + fmt.Printf("Tron Address: %s\n", addr) + } + return err + }, + } + cmdImportKS.Flags().BoolVar(&userProvidesPassphrase, "passphrase", false, ppPrompt) + cmdImportKS.Flags().StringVar(&passphraseFilePath, "passphrase-file", "", "path to a file containing the passphrase") + cmdImportKS.Flags().BoolVar(&quietImport, "quiet", false, "do not print out imported account name") + + cmdImportPK := &cobra.Command{ + Use: "import-private-key [ACCOUNT_NAME]", + Short: "Import an existing keystore key (only accept secp256k1 private keys)", + Args: cobra.RangeArgs(1, 2), + RunE: func(cmd *cobra.Command, args []string) error { + userName := "" + if len(args) == 2 { + userName = args[1] + } + passphrase, err := getPassphrase() + if err != nil { + return err + } + name, err := account.ImportFromPrivateKey(args[0], userName, passphrase) + if !quietImport && err == nil { + fmt.Printf("Imported keystore given account alias of `%s`\n", name) + addr, _ := store.AddressFromAccountName(name) + fmt.Printf("Tron Address: %s\n", addr) + } + return err + }, + } + cmdImportPK.Flags().BoolVar(&userProvidesPassphrase, "passphrase", false, ppPrompt) + cmdImportPK.Flags().BoolVar(&quietImport, "quiet", false, "do not print out imported account name") + + cmdExportPK := &cobra.Command{ + Use: "export-private-key ", + Short: "Export the secp256k1 private key", + Args: cobra.ExactArgs(1), + PreRunE: validateAddress, + RunE: func(cmd *cobra.Command, args []string) error { + passphrase, err := getPassphrase() + if err != nil { + return err + } + return account.ExportPrivateKey(addr.address, passphrase) + }, + } + cmdExportPK.Flags().BoolVar(&userProvidesPassphrase, "passphrase", false, ppPrompt) + cmdExportPK.Flags().StringVar(&passphraseFilePath, "passphrase-file", "", "path to a file containing the passphrase") + + cmdExportKS := &cobra.Command{ + Use: "export-ks ", + Short: "Export the keystore file contents", + Args: cobra.ExactArgs(2), + PreRunE: validateAddress, + RunE: func(cmd *cobra.Command, args []string) error { + passphrase, err := getPassphrase() + if err != nil { + return err + } + file, e := account.ExportKeystore(addr.address, args[1], passphrase) + if file != "" { + fmt.Println("Exported keystore to", file) + } + return e + }, + } + cmdExportKS.Flags().BoolVar(&userProvidesPassphrase, "passphrase", false, ppPrompt) + cmdExportKS.Flags().StringVar(&passphraseFilePath, "passphrase-file", "", "path to a file containing the passphrase") + + return []*cobra.Command{cmdList, cmdLocation, cmdAdd, cmdRemove, cmdMnemonic, cmdRecoverMnemonic, cmdImportKS, cmdImportPK, + cmdExportKS, cmdExportPK} +} + +func init() { + cmdKeys := &cobra.Command{ + Use: "keys", + Short: "Add or view local private keys", + Long: "Manage your local keys", + RunE: func(cmd *cobra.Command, args []string) error { + cmd.Help() + return nil + }, + } + + cmdKeys.AddCommand(keysSub()...) + RootCmd.AddCommand(cmdKeys) +} diff --git a/cmd/subcommands/root.go b/cmd/subcommands/root.go new file mode 100644 index 00000000..dfe1145a --- /dev/null +++ b/cmd/subcommands/root.go @@ -0,0 +1,218 @@ +package cmd + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "os" + "path" + "regexp" + "strings" + "time" + + color "github.com/fatih/color" + "github.com/fbsobreira/gotron-sdk/pkg/client" + "github.com/fbsobreira/gotron-sdk/pkg/client/transaction" + "github.com/fbsobreira/gotron-sdk/pkg/common" + "github.com/fbsobreira/gotron-sdk/pkg/store" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/cobra/doc" +) + +const defaultTimeout = 40 + +var ( + addr tronAddress + signer string + signerAddress tronAddress + verbose bool + dryRun bool + useLedgerWallet bool + noPrettyOutput bool + node string + keyStoreDir string + givenFilePath string + timeout uint32 + conn *client.GrpcClient + // RootCmd is single entry point of the CLI + RootCmd = &cobra.Command{ + Use: "tronctl", + Short: "Tron Blokchain Controller ", + SilenceUsage: true, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + if verbose { + common.EnableAllVerbose() + } + switch URLcomponents := strings.Split(node, ":"); len(URLcomponents) { + case 1: + node = node + ":50051" + } + conn = client.NewGrpcClient(node) + if err := conn.Start(); err != nil { + return err + } + + if len(signer) > 0 { + var err error + if signerAddress, err = findAddress(signer); err != nil { + return err + } + } + + return nil + }, + Long: fmt.Sprintf(` +CLI interface to Tron blockchain + +%s`, g("type 'tronclt --help' for details")), + RunE: func(cmd *cobra.Command, args []string) error { + cmd.Help() + return nil + }, + } +) + +func init() { + vS := "dump out debug information, same as env var GOTRON_SDK_DEBUG=true" + RootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, vS) + RootCmd.PersistentFlags().StringVarP(&signer, "signer", "s", "", "") + RootCmd.PersistentFlags().StringVarP(&node, "node", "n", defaultNodeAddr, "") + RootCmd.PersistentFlags().BoolVar( + &noPrettyOutput, "no-pretty", false, "Disable pretty print JSON outputs", + ) + RootCmd.Flags().BoolVar(&dryRun, "dry-run", false, "do not send signed transaction") + RootCmd.Flags().Uint32Var(&timeout, "timeout", defaultTimeout, "set timeout in seconds. Set to 0 to not wait for confirm") + + RootCmd.PersistentFlags().BoolVarP(&useLedgerWallet, "ledger", "e", false, "Use ledger hardware wallet") + RootCmd.PersistentFlags().StringVar(&givenFilePath, "file", "", "Path to file for given command when applicable") + RootCmd.AddCommand(&cobra.Command{ + Use: "docs", + Short: fmt.Sprintf("Generate docs to a local %s directory", tronctlDocsDir), + RunE: func(cmd *cobra.Command, args []string) error { + cwd, _ := os.Getwd() + docDir := path.Join(cwd, tronctlDocsDir) + os.Mkdir(docDir, 0700) + err := doc.GenMarkdownTree(RootCmd, docDir) + return err + }, + }) +} + +var ( + // VersionWrapDump meant to be set from main.go + VersionWrapDump = "" + versionLink = "https://api.github.com/repos/fbsobreira/gotron-sdk/releases/latest" + versionTagLink = "https://api.github.com/repos/fbsobreira/gotron-sdk/git/ref/tags/" + versionFormat = regexp.MustCompile("v[0-9]+-[a-z0-9]{7}") +) + +//GitHubReleaseAssets json struct +type GitHubReleaseAssets struct { + ID json.Number `json:"id"` + Name string `json:"name"` + Size json.Number `json:"size"` + URL string `json:"browser_download_url"` +} + +//GitHubRelease json struct +type GitHubRelease struct { + Prerelease bool `json:"prerelease"` + TagName string `json:"tag_name"` + TargetCommitish string `json:"target_commitish"` + CreatedAt time.Time `json:"created_at"` + Assets []GitHubReleaseAssets `json:"assets"` +} + +//GitHubTag json struct +type GitHubTag struct { + Ref string `json:"ref"` + NodeID string `json:"node_id"` + URL string `json:"url"` + DATA struct { + SHA string `json:"sha"` + } `json:"object"` +} + +func getGitVersion() (string, error) { + resp, _ := http.Get(versionLink) + defer resp.Body.Close() + // if error, no op + if resp != nil && resp.StatusCode == 200 { + buf := new(bytes.Buffer) + buf.ReadFrom(resp.Body) + release := &GitHubRelease{} + if err := json.Unmarshal(buf.Bytes(), release); err != nil { + return "", err + } + + respTag, _ := http.Get(versionTagLink + release.TagName) + defer resp.Body.Close() + // if error, no op + if respTag != nil && respTag.StatusCode == 200 { + buf.Reset() + buf.ReadFrom(respTag.Body) + + releaseTag := &GitHubTag{} + if err := json.Unmarshal(buf.Bytes(), releaseTag); err != nil { + return "", err + } + commit := strings.Split(VersionWrapDump, "-") + + if releaseTag.DATA.SHA[:8] != commit[1] { + warnMsg := fmt.Sprintf("Warning: Using outdated version. Redownload to upgrade to %s\n", release.TagName) + fmt.Fprintf(os.Stderr, color.RedString(warnMsg)) + return release.TagName, fmt.Errorf(warnMsg) + } + return release.TagName, nil + } + } + return "", fmt.Errorf("could not fetch version") +} + +// Execute kicks off the tronctl CLI +func Execute() { + RootCmd.SilenceErrors = true + if err := RootCmd.Execute(); err != nil { + if tag, errGit := getGitVersion(); errGit == nil { + VersionWrapDump += ":" + tag + } + errMsg := errors.Wrapf(err, "commit: %s, error", VersionWrapDump).Error() + fmt.Fprintf(os.Stderr, errMsg+"\n") + fmt.Fprintf(os.Stderr, "try adding a `--help` flag\n") + os.Exit(1) + } +} + +func validateAddress(cmd *cobra.Command, args []string) error { + // Check if input valid one address + var err error + addr, err = findAddress(args[0]) + return err +} + +func findAddress(value string) (tronAddress, error) { + // Check if input valid one address + address := tronAddress{} + if err := address.Set(value); err != nil { + // Check if input is valid account name + if acc, err := store.AddressFromAccountName(value); err == nil { + return tronAddress{acc}, nil + } + return address, fmt.Errorf("Invalid address/Invalid account name: %s", value) + } + return address, nil +} + +func opts(ctlr *transaction.Controller) { + if dryRun { + ctlr.Behavior.DryRun = true + } + if useLedgerWallet { + ctlr.Behavior.SigningImpl = transaction.Ledger + } + if timeout > 0 { + ctlr.Behavior.ConfirmationWaitTime = timeout + } +} diff --git a/cmd/subcommands/utility.go b/cmd/subcommands/utility.go new file mode 100644 index 00000000..ff912c94 --- /dev/null +++ b/cmd/subcommands/utility.go @@ -0,0 +1,55 @@ +package cmd + +import ( + "fmt" + + "github.com/fbsobreira/gotron-sdk/pkg/address" + "github.com/spf13/cobra" +) + +func init() { + cmdUtilities := &cobra.Command{ + Use: "utility", + Short: "common tron utilities", + RunE: func(cmd *cobra.Command, args []string) error { + cmd.Help() + return nil + }, + } + + cmdUtilities.AddCommand([]*cobra.Command{{ + Use: "metadata", + Short: "data includes network specific values", + RunE: func(cmd *cobra.Command, args []string) error { + return nil + }, + }, { + Use: "metrics", + Short: "mostly in-memory fluctuating values", + RunE: func(cmd *cobra.Command, args []string) error { + return nil + }, + }, { + Use: "base58-to-addr", + Args: cobra.ExactArgs(1), + Short: "0x Address of a base58 one-address", + RunE: func(cmd *cobra.Command, args []string) error { + addr, err := address.Base58ToAddress(args[0]) + if err != nil { + return err + } + fmt.Println(addr.Hex()) + return nil + }, + }, { + Use: "addr-to-base58", + Args: cobra.ExactArgs(1), + Short: "base58 tron-address of an 0x address", + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Println(address.HexToAddress(args[0])) + return nil + }, + }}...) + + RootCmd.AddCommand(cmdUtilities) +} diff --git a/cmd/subcommands/values.go b/cmd/subcommands/values.go new file mode 100644 index 00000000..e01e2af6 --- /dev/null +++ b/cmd/subcommands/values.go @@ -0,0 +1,12 @@ +package cmd + +import "github.com/fatih/color" + +const ( + tronctlDocsDir = "tronctl-docs" + defaultNodeAddr = "grpc.trongrid.io:50051" +) + +var ( + g = color.New(color.FgGreen).SprintFunc() +) diff --git a/common/base58/base58_test.go b/common/base58/base58_test.go deleted file mode 100644 index 72c044af..00000000 --- a/common/base58/base58_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package base58 - -import ( - "github.com/fbsobreira/gotron/common/hexutil" - "strings" - "testing" -) - -func TestEncode(t *testing.T) { -} - -func TestDecodeCheck(t *testing.T) { - decodeBytes := DecodeCheck("27ZESitosJfKouTBrGg6Nk5yEjnJHXMbkZp") - - decode := hexutil.Encode(decodeBytes) - - if strings.EqualFold(decode, "a06f61d05912402335744c288d4b72a735ede35604") { - t.Log("success") - } else { - t.Fatalf("failure: %s", decode) - } -} diff --git a/common/crypto/crypto.go b/common/crypto/crypto.go deleted file mode 100644 index e19a3f2b..00000000 --- a/common/crypto/crypto.go +++ /dev/null @@ -1,153 +0,0 @@ -package crypto - -import ( - "crypto/ecdsa" - "crypto/rand" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/crypto" - "github.com/fbsobreira/gotron/common/base58" - "github.com/fbsobreira/gotron/common/hexutil" -) - -// Lengths of hashes and addresses in bytes. -const ( - // HashLength is the expected length of the hash - HashLength = 32 - // AddressLength is the expected length of the address - AddressLength = 21 - // Base58AddressLength is the expected length of the address - Base58AddressLength = 34 - // AmountDecimalPoint TRX decimal point - AmountDecimalPoint = 6 -) - -// AddressPrefix is the byte prefix of the address used in TRON addresses. -// It's supposed to be '0xa0' for testnet, and '0x41' for mainnet. -// But the Shasta mainteiners don't use the testnet params. So the default value is 41. -// You may change it directly, or use the SetAddressPrefix/UseMainnet/UseTestnet methods. -var AddressPrefix = byte(0x41) - -// AddressPrefixHex address in hex string. -var AddressPrefixHex = hexutil.ToHex([]byte{AddressPrefix}) - -// Hash represents the 32 byte Keccak256 hash of arbitrary data. -type Hash [HashLength]byte - -// BytesToHash sets b to hash. -// If b is larger than len(h), b will be cropped from the left. -func BytesToHash(b []byte) Hash { - var h Hash - h.SetBytes(b) - return h -} - -// BigToHash sets byte representation of b to hash. -// If b is larger than len(h), b will be cropped from the left. -func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) } - -// HexToHash sets byte representation of s to hash. -// If b is larger than len(h), b will be cropped from the left. -func HexToHash(s string) Hash { return BytesToHash(hexutil.FromHex(s)) } - -// Bytes gets the byte representation of the underlying hash. -func (h Hash) Bytes() []byte { return h[:] } - -// Big converts a hash to a big integer. -func (h Hash) Big() *big.Int { return new(big.Int).SetBytes(h[:]) } - -// Hex converts a hash to a hex string. -func (h Hash) Hex() string { return hexutil.Encode(h[:]) } - -// TerminalString implements log.TerminalStringer, formatting a string for console -// output during logging. -func (h Hash) TerminalString() string { - return fmt.Sprintf("%x…%x", h[:3], h[29:]) -} - -// String implements the stringer interface and is used also by the logger when -// doing full logging into a file. -func (h Hash) String() string { - return h.Hex() -} - -// SetBytes sets the hash to the value of b. -// If b is larger than len(h), b will be cropped from the left. -func (h *Hash) SetBytes(b []byte) { - if len(b) > len(h) { - b = b[len(b)-HashLength:] - } - - copy(h[HashLength-len(b):], b) -} - -// SetAddressPrefix sets the prefix to the provided byte. -func SetAddressPrefix(p byte) { - AddressPrefix = p - AddressPrefixHex = hexutil.ToHex([]byte{p}) -} - -// UseMainnet sets the address prefix used for the main net. -func UseMainnet() { - SetAddressPrefix(0x41) -} - -// UseTestnet sets the address prefix used for the test net. -func UseTestnet() { - SetAddressPrefix(0xa0) -} - -/////////// Address - -type Address [AddressLength]byte - -func (a Address) Bytes() []byte { - return a[:] -} - -func (a *Address) SetBytes(b []byte) { - if len(b) > len(a) { - b = b[len(b)-AddressLength:] - } - copy(a[AddressLength-len(b):], b) -} - -func BytesToAddress(b []byte) Address { - var a Address - a.SetBytes(b) - return a -} - -// BigToAddress returns Address with byte values of b. -// If b is larger than len(h), b will be cropped from the left. -func BigToAddress(b *big.Int) Address { return BytesToAddress(b.Bytes()) } - -// HexToAddress returns Address with byte values of s. -// If s is larger than len(h), s will be cropped from the left. -func HexToAddress(s string) Address { return BytesToAddress(hexutil.FromHex(s)) } - -// String implements fmt.Stringer. -func (a Address) String() string { - return base58.EncodeCheck(a.Bytes()) -} - -func GenerateKey() (*ecdsa.PrivateKey, error) { - return ecdsa.GenerateKey(crypto.S256(), rand.Reader) -} - -func GetPrivateKeyByHexString(privateKeyHexString string) (*ecdsa.PrivateKey, - error) { - return crypto.HexToECDSA(privateKeyHexString) -} - -func PubkeyToAddress(p ecdsa.PublicKey) Address { - address := crypto.PubkeyToAddress(p) - - addressTron := make([]byte, AddressLength) - - addressTron = append(addressTron, AddressPrefix) - addressTron = append(addressTron, address.Bytes()...) - - return BytesToAddress(addressTron) -} diff --git a/common/crypto/crypto_test.go b/common/crypto/crypto_test.go deleted file mode 100644 index 992278a7..00000000 --- a/common/crypto/crypto_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package crypto - -import ( - "fmt" - "github.com/fbsobreira/gotron/common/hexutil" - "testing" -) - -func TestGenerateKey(t *testing.T) { - k, err := GenerateKey() - - if err != nil { - t.Error(err.Error()) - } - - priv := k.D.Text(16) - fmt.Println(priv) - - k2, err := GetPrivateKeyByHexString(priv) - if err != nil { - t.Error(err.Error()) - } - priv2 := k2.D.Text(16) - fmt.Println(priv2) -} - -func TestGetPrivateKeyByHexString(t *testing.T) { - _, err := GetPrivateKeyByHexString("f5b1c865e615d584eb2a9234b95cd749cfbf0dc69d6e75584052812ba5b71418") - - if err != nil { - t.Error(err.Error()) - } -} - -func TestPubkeyToAddress(t *testing.T) { - privateKey, err := GetPrivateKeyByHexString( - "f5b1c865e615d584eb2a9234b95cd749cfbf0dc69d6e75584052812ba5b71418") - - if err != nil { - t.Error(err.Error()) - } - - address := PubkeyToAddress(privateKey.PublicKey) - - t.Log(hexutil.Encode(address.Bytes())) -} diff --git a/common/crypto/signature.go b/common/crypto/signature.go deleted file mode 100644 index 00a26426..00000000 --- a/common/crypto/signature.go +++ /dev/null @@ -1,14 +0,0 @@ -package crypto - -import ( - "crypto/ecdsa" - "github.com/ethereum/go-ethereum/crypto" -) - -func Sign(hash []byte, privateKey *ecdsa.PrivateKey) ([]byte, error) { - return crypto.Sign(hash, privateKey) -} - -func VerifySignature(publicKey, hash, signature []byte) bool { - return crypto.VerifySignature(publicKey, hash, signature) -} diff --git a/common/global/global.go b/common/global/global.go deleted file mode 100644 index 1f40c2ff..00000000 --- a/common/global/global.go +++ /dev/null @@ -1,5 +0,0 @@ -package global - -import "github.com/fbsobreira/gotron/service" - -var TronClient *service.GrpcClient diff --git a/common/hexutil/hexutils_test.go b/common/hexutil/hexutils_test.go deleted file mode 100644 index 3d177ce3..00000000 --- a/common/hexutil/hexutils_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package hexutil - -import ( - "bytes" - "testing" -) - -type marshalTest struct { - input interface{} - want string -} - -type unmarshalTest struct { - input string - want interface{} - wantErr error // if set, decoding must fail on any platform - wantErr32bit error // if set, decoding must fail on 32bit platforms (used for Uint tests) -} - -var ( - encodeBytesTests = []marshalTest{ - {[]byte{}, ""}, - {[]byte{0}, "00"}, - {[]byte{0, 0, 1, 2}, "00000102"}, - } - - decodeBytesTests = []unmarshalTest{ - // invalid - {input: ``, wantErr: EmptyString}, - // valid - {input: `02`, want: []byte{0x02}}, - {input: `ffffffffff`, want: []byte{0xff, 0xff, 0xff, 0xff, 0xff}}, - { - input: `ffffffffffffffffffffffffffffffffffff`, - want: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, - }, - } -) - -func checkError(t *testing.T, input string, got, want error) bool { - if got == nil { - if want != nil { - t.Errorf("input %s: got no error, want %q", input, want) - return false - } - return true - } - if want == nil { - t.Errorf("input %s: unexpected error %q", input, got) - } else if got.Error() != want.Error() { - t.Errorf("input %s: got error %q, want %q", input, got, want) - } - return false -} - -func TestEncode(t *testing.T) { - for _, test := range encodeBytesTests { - enc := Encode(test.input.([]byte)) - if enc != test.want { - t.Errorf("input %x: wrong encoding %s", test.input, enc) - } - } -} - -func TestDecode(t *testing.T) { - for _, test := range decodeBytesTests { - dec, err := Decode(test.input) - if !checkError(t, test.input, err, test.wantErr) { - continue - } - if !bytes.Equal(test.want.([]byte), dec) { - t.Errorf("input %s: value mismatch: got %x, want %x", test.input, dec, test.want) - continue - } - } -} diff --git a/conf/app.conf b/conf/app.conf deleted file mode 100644 index 8afb6226..00000000 --- a/conf/app.conf +++ /dev/null @@ -1,8 +0,0 @@ -appname = gotron -httpport = 8080 -runmode = dev -autorender = false -copyrequestbody = true -EnableDocs = true - -grpcaddress = 54.236.37.243:50051 \ No newline at end of file diff --git a/controllers/account.go b/controllers/account.go deleted file mode 100644 index a7e9739f..00000000 --- a/controllers/account.go +++ /dev/null @@ -1,71 +0,0 @@ -package controllers - -import ( - "github.com/fbsobreira/gotron/models" - - "encoding/json" - "github.com/astaxie/beego" - "github.com/fbsobreira/gotron/models/contract" -) - -// Operations about Account -type AccountController struct { - beego.Controller -} - -// @Title Get account -// @Description get account by address -// @Param address path string true "The key for staticblock" -// @Success 200 {account} models.Account -// @Failure 403 :address is empty -// @router /address/:address [get] -func (a *AccountController) Get() { - address := a.GetString(":address") - if address != "" { - account, err := models.GetAccountByAddress(address) - if err != nil { - a.Data["json"] = err.Error() - } else { - a.Data["json"] = account - } - } - a.ServeJSON() -} - -// @Title Get account net message -// @Description get account net message by address -// @Param address path string true -// @Success 200 {accountnetmessage} models.AccountNetMessage -// @Failure 403 :address is empty -// @router /net-message/address/:address [get] -func (a *AccountController) NetMessage() { - address := a.GetString(":address") - if address != "" { - accountNetMessage := models.GetAccountNet(address) - a.Data["json"] = accountNetMessage - } - a.ServeJSON() -} - -// @Title Create account -// @Param owneraddress body string true -// @Param accountaddress body string true -// @router /create [post] -func (a *AccountController) CreateAccount() { - var accountCreateContract contract.AccountCreateContract - err := json.Unmarshal(a.Ctx.Input.RequestBody, &accountCreateContract) - - if err != nil { - a.Data["json"] = err.Error() - } else { - transaction, err := contract.CreateAccount(accountCreateContract) - - if err != nil { - a.Data["json"] = err.Error() - } else { - a.Data["json"] = transaction - } - } - - a.ServeJSON() -} diff --git a/controllers/assetissue.go b/controllers/assetissue.go deleted file mode 100644 index 6bb5a33b..00000000 --- a/controllers/assetissue.go +++ /dev/null @@ -1,52 +0,0 @@ -package controllers - -import ( - "github.com/fbsobreira/gotron/models" - - "github.com/astaxie/beego" -) - -// Operations about asset issue -type AssetIssueController struct { - beego.Controller -} - -// @Title Get asset issue list -// @Description get asset issue list by account -// @Param address path string true -// @Success 200 {assetissuelist} models.AssetIssueList -// @Failure 403 :address is empty -// @router /address/:address [get] -func (i *AssetIssueController) Address() { - address := i.GetString(":address") - if address != "" { - assetIssueList := models.GetAssetIssueAccount(address) - i.Data["json"] = assetIssueList - } - i.ServeJSON() -} - -// @Title Get asset issue by name -// @Description get asset issue by name -// @Param name path string true -// @Success 200 {assetissue} models.AssetIssueContract -// @Failure 403 :name is empty -// @router /name/:name [get] -func (i *AssetIssueController) Name() { - name := i.GetString(":name") - if name != "" { - assetIssue := models.GetAssetIssueByName(name) - i.Data["json"] = assetIssue - } - i.ServeJSON() -} - -// @Title Get asset issue list -// @Description get asset issue list -// @Success 200 {assetissuelist} models.AssetIssueList -// @router /list [get] -func (i *AssetIssueController) List() { - assetIssueList := models.GetAssetIssueList() - i.Data["json"] = assetIssueList - i.ServeJSON() -} diff --git a/controllers/block.go b/controllers/block.go deleted file mode 100644 index 9b499ae2..00000000 --- a/controllers/block.go +++ /dev/null @@ -1,92 +0,0 @@ -package controllers - -import ( - "github.com/fbsobreira/gotron/models" - - "github.com/astaxie/beego" -) - -// Operations about Block -type BlockController struct { - beego.Controller -} - -// @Title Get now block -// @Description get now block account -// @Success 200 {block} models.Block -// @router /now [get] -func (b *BlockController) Now() { - nowBlock := models.GetNowBlock() - b.Data["json"] = nowBlock - b.ServeJSON() -} - -// @Title Get block by num -// @Description Get block by num -// @Param num path int64 true -// @Success 200 {block} models.Block -// @router /num/:num [get] -func (b *BlockController) Num() { - num, err := b.GetInt64(":num") - if err != nil { - b.Data["json"] = err.Error() - } else { - block := models.GetBlockByNum(num) - b.Data["json"] = block - } - b.ServeJSON() -} - -// @Title Get block by id -// @Description Get block by id -// @Param id path string true -// @Success 200 {block} models.Block -// @router /id/:id [get] -func (b *BlockController) Id() { - id := b.GetString(":id") - if id != "" { - block := models.GetBlockById(id) - b.Data["json"] = block - } - b.ServeJSON() -} - -// @Title Get block list -// @Description Get block list -// @Param start path int64 true -// @Param end path int64 true -// @Success 200 {blocklist} models.BlockList -// @router /start/:start/end/:end [get] -func (b *BlockController) GetBlockByLimit() { - start, err := b.GetInt64(":start") - - if err != nil { - b.Data["json"] = err.Error() - } else { - end, err := b.GetInt64(":end") - if err != nil { - b.Data["json"] = err.Error() - } else { - blockList := models.GetBlockByLimitNext(start, end) - b.Data["json"] = blockList - } - } - - b.ServeJSON() -} - -// @Title Get block list -// @Description Get block list by latest num -// @Param num path int64 true -// @Success 200 {blocklist} models.BlockList -// @router /latest-num/:num [get] -func (b *BlockController) LatestNum() { - num, err := b.GetInt64(":num") - if err != nil { - b.Data["json"] = err.Error() - } else { - block := models.GetBlockByLatestNum(num) - b.Data["json"] = block - } - b.ServeJSON() -} diff --git a/controllers/node.go b/controllers/node.go deleted file mode 100644 index 15311ce2..00000000 --- a/controllers/node.go +++ /dev/null @@ -1,22 +0,0 @@ -package controllers - -import ( - "github.com/fbsobreira/gotron/models" - - "github.com/astaxie/beego" -) - -// Operations about Node -type NodeController struct { - beego.Controller -} - -// @Title Get node list -// @Description get node list -// @Success 200 {nodeList} []models.Node -// @router /list [get] -func (n *NodeController) List() { - nodes := models.GetNodeList() - n.Data["json"] = nodes - n.ServeJSON() -} diff --git a/controllers/numbermessage.go b/controllers/numbermessage.go deleted file mode 100644 index cf07fe9d..00000000 --- a/controllers/numbermessage.go +++ /dev/null @@ -1,32 +0,0 @@ -package controllers - -import ( - "github.com/fbsobreira/gotron/models" - - "github.com/astaxie/beego" -) - -// Operations about Number Message -type NumberMessageController struct { - beego.Controller -} - -// @Title Get next maintenance time -// @Description Get next maintenance time -// @Success 200 {nextmaintenancetime} models.NumberMessage -// @router /next-maintenance-time [get] -func (n *NumberMessageController) NextMaintenanceTime() { - nextMaintenanceTime := models.GetNextMaintenanceTime() - n.Data["json"] = nextMaintenanceTime - n.ServeJSON() -} - -// @Title Get total transaction -// @Description Get total transaction -// @Success 200 {totaltransaction} models.NumberMessage -// @router /total-transaction [get] -func (n *NumberMessageController) TotalTransaction() { - totalTransaction := models.GetTotalTransaction() - n.Data["json"] = totalTransaction - n.ServeJSON() -} diff --git a/controllers/object.go b/controllers/object.go deleted file mode 100644 index 43707152..00000000 --- a/controllers/object.go +++ /dev/null @@ -1,91 +0,0 @@ -package controllers - -import ( - "encoding/json" - - "github.com/astaxie/beego" - "github.com/fbsobreira/gotron/models" -) - -// Operations about object -type ObjectController struct { - beego.Controller -} - -// @Title Create -// @Description create object -// @Param body body models.Object true "The object content" -// @Success 200 {string} models.Object.Id -// @Failure 403 body is empty -// @router / [post] -func (o *ObjectController) Post() { - var ob models.Object - json.Unmarshal(o.Ctx.Input.RequestBody, &ob) - objectid := models.AddOne(ob) - o.Data["json"] = map[string]string{"ObjectId": objectid} - o.ServeJSON() -} - -// @Title Get -// @Description find object by objectid -// @Param objectId path string true "the objectid you want to get" -// @Success 200 {object} models.Object -// @Failure 403 :objectId is empty -// @router /:objectId [get] -func (o *ObjectController) Get() { - objectId := o.Ctx.Input.Param(":objectId") - if objectId != "" { - ob, err := models.GetOne(objectId) - if err != nil { - o.Data["json"] = err.Error() - } else { - o.Data["json"] = ob - } - } - o.ServeJSON() -} - -// @Title GetAll -// @Description get all objects -// @Success 200 {object} models.Object -// @Failure 403 :objectId is empty -// @router / [get] -func (o *ObjectController) GetAll() { - obs := models.GetAll() - o.Data["json"] = obs - o.ServeJSON() -} - -// @Title Update -// @Description update the object -// @Param objectId path string true "The objectid you want to update" -// @Param body body models.Object true "The body" -// @Success 200 {object} models.Object -// @Failure 403 :objectId is empty -// @router /:objectId [put] -func (o *ObjectController) Put() { - objectId := o.Ctx.Input.Param(":objectId") - var ob models.Object - json.Unmarshal(o.Ctx.Input.RequestBody, &ob) - - err := models.Update(objectId, ob.Score) - if err != nil { - o.Data["json"] = err.Error() - } else { - o.Data["json"] = "update success!" - } - o.ServeJSON() -} - -// @Title Delete -// @Description delete the object -// @Param objectId path string true "The objectId you want to delete" -// @Success 200 {string} delete success! -// @Failure 403 objectId is empty -// @router /:objectId [delete] -func (o *ObjectController) Delete() { - objectId := o.Ctx.Input.Param(":objectId") - models.Delete(objectId) - o.Data["json"] = "delete success!" - o.ServeJSON() -} diff --git a/controllers/transaction.go b/controllers/transaction.go deleted file mode 100644 index 12851c46..00000000 --- a/controllers/transaction.go +++ /dev/null @@ -1,26 +0,0 @@ -package controllers - -import ( - "github.com/fbsobreira/gotron/models" - - "github.com/astaxie/beego" -) - -// Operations about Transaction -type TransactionController struct { - beego.Controller -} - -// @Title Get transaction by id -// @Description Get transaction by id -// @Param id path string true -// @Success 200 {transaction} models.Transaction -// @router /id/:id [get] -func (b *TransactionController) Id() { - id := b.GetString(":id") - if id != "" { - transaction := models.GetTransactionById(id) - b.Data["json"] = transaction - } - b.ServeJSON() -} diff --git a/controllers/user.go b/controllers/user.go deleted file mode 100644 index 138270ee..00000000 --- a/controllers/user.go +++ /dev/null @@ -1,118 +0,0 @@ -package controllers - -import ( - "encoding/json" - "github.com/fbsobreira/gotron/models" - - "github.com/astaxie/beego" -) - -// Operations about Users -type UserController struct { - beego.Controller -} - -// @Title CreateUser -// @Description create users -// @Param body body models.User true "body for user content" -// @Success 200 {int} models.User.Id -// @Failure 403 body is empty -// @router / [post] -func (u *UserController) Post() { - var user models.User - json.Unmarshal(u.Ctx.Input.RequestBody, &user) - uid := models.AddUser(user) - u.Data["json"] = map[string]string{"uid": uid} - u.ServeJSON() -} - -// @Title GetAll -// @Description get all Users -// @Success 200 {object} models.User -// @router / [get] -func (u *UserController) GetAll() { - users := models.GetAllUsers() - u.Data["json"] = users - u.ServeJSON() -} - -// @Title Get -// @Description get user by uid -// @Param uid path string true "The key for staticblock" -// @Success 200 {object} models.User -// @Failure 403 :uid is empty -// @router /:uid [get] -func (u *UserController) Get() { - uid := u.GetString(":uid") - if uid != "" { - user, err := models.GetUser(uid) - if err != nil { - u.Data["json"] = err.Error() - } else { - u.Data["json"] = user - } - } - u.ServeJSON() -} - -// @Title Update -// @Description update the user -// @Param uid path string true "The uid you want to update" -// @Param body body models.User true "body for user content" -// @Success 200 {object} models.User -// @Failure 403 :uid is not int -// @router /:uid [put] -func (u *UserController) Put() { - uid := u.GetString(":uid") - if uid != "" { - var user models.User - json.Unmarshal(u.Ctx.Input.RequestBody, &user) - uu, err := models.UpdateUser(uid, &user) - if err != nil { - u.Data["json"] = err.Error() - } else { - u.Data["json"] = uu - } - } - u.ServeJSON() -} - -// @Title Delete -// @Description delete the user -// @Param uid path string true "The uid you want to delete" -// @Success 200 {string} delete success! -// @Failure 403 uid is empty -// @router /:uid [delete] -func (u *UserController) Delete() { - uid := u.GetString(":uid") - models.DeleteUser(uid) - u.Data["json"] = "delete success!" - u.ServeJSON() -} - -// @Title Login -// @Description Logs user into the system -// @Param username query string true "The username for login" -// @Param password query string true "The password for login" -// @Success 200 {string} login success -// @Failure 403 user not exist -// @router /login [get] -func (u *UserController) Login() { - username := u.GetString("username") - password := u.GetString("password") - if models.Login(username, password) { - u.Data["json"] = "login success" - } else { - u.Data["json"] = "user not exist" - } - u.ServeJSON() -} - -// @Title logout -// @Description Logs out current logged in user session -// @Success 200 {string} logout success -// @router /logout [get] -func (u *UserController) Logout() { - u.Data["json"] = "logout success" - u.ServeJSON() -} diff --git a/controllers/witness.go b/controllers/witness.go deleted file mode 100644 index fe77a404..00000000 --- a/controllers/witness.go +++ /dev/null @@ -1,22 +0,0 @@ -package controllers - -import ( - "github.com/fbsobreira/gotron/models" - - "github.com/astaxie/beego" -) - -// Operations about Witness -type WitnessController struct { - beego.Controller -} - -// @Title Get witness list -// @Description get witness list -// @Success 200 {witnessList} []models.Witness -// @router /list [get] -func (w *WitnessController) List() { - witnesses := models.GetWitnessList() - w.Data["json"] = witnesses - w.ServeJSON() -} diff --git a/e2e/keystore_test.go b/e2e/keystore_test.go new file mode 100644 index 00000000..ae38b91c --- /dev/null +++ b/e2e/keystore_test.go @@ -0,0 +1,11 @@ +package keys + +import ( + "fmt" + "testing" +) + +func TestKeyStore(t *testing.T) { + fmt.Println("Hello world") + t.Errorf("Testing pipeline") +} diff --git a/gen-proto b/gen-proto.sh old mode 100755 new mode 100644 similarity index 97% rename from gen-proto rename to gen-proto.sh index 3dd54bf5..c471c282 --- a/gen-proto +++ b/gen-proto.sh @@ -1,3 +1,4 @@ +#!/bin/bash protoc -I=./protocol -I/usr/lib -I./third_party/googleapis --go_out=plugins=grpc,paths=source_relative:. ./protocol/core/*.proto ./protocol/core/contract/*.proto protoc -I=./protocol -I/usr/lib -I./third_party/googleapis --go_out=plugins=grpc,paths=source_relative:. ./protocol/api/*.proto diff --git a/glide.yaml b/glide.yaml deleted file mode 100644 index 952143dd..00000000 --- a/glide.yaml +++ /dev/null @@ -1,13 +0,0 @@ -package: github.com/fbsobreira/gotron -import: -- package: github.com/golang/protobuf - subpackages: - - protoc-gen-go - - proto -- package: google.golang.org/grpc -- package: golang.org/x/net -- package: github.com/shengdoushi/base58 -- package: github.com/ethereum/go-ethereum - subpackages: - - crypto -- package: github.com/astaxie/beego diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..465343e2 --- /dev/null +++ b/go.mod @@ -0,0 +1,24 @@ +module github.com/fbsobreira/gotron-sdk + +go 1.14 + +require ( + github.com/btcsuite/btcd v0.20.1-beta + github.com/cosmos/cosmos-sdk v0.38.3 + github.com/deckarep/golang-set v1.7.1 + github.com/ethereum/go-ethereum v1.9.13 + github.com/fatih/color v1.9.0 + github.com/golang/protobuf v1.4.0 + github.com/karalabe/hid v1.0.0 + github.com/mitchellh/go-homedir v1.1.0 + github.com/pborman/uuid v1.2.0 + github.com/pkg/errors v0.9.1 + github.com/rjeczalik/notify v0.9.2 + github.com/shengdoushi/base58 v1.0.0 + github.com/spf13/cobra v1.0.0 + github.com/tyler-smith/go-bip39 v1.0.2 + go.uber.org/zap v1.15.0 + golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 + google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84 + google.golang.org/grpc v1.29.1 +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..94139768 --- /dev/null +++ b/go.sum @@ -0,0 +1,622 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/99designs/keyring v1.1.3/go.mod h1:657DQuMrBZRtuL/voxVyiyb6zpMehlm5vLB9Qwrv904= +github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= +github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= +github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/ChainSafe/go-schnorrkel v0.0.0-20200102211924-4bcbc698314f/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/VictoriaMetrics/fastcache v1.5.3/go.mod h1:+jv9Ckb+za/P1ZRg/sulP5Ni1v49daAVERr0H3CuscE= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/Workiva/go-datastructures v1.0.52/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847 h1:rtI0fD4oG/8eVokGVPYJEW1F88p1ZNgXiEIs9thEE4A= +github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d/go.mod h1:icNx/6QdFblhsEjZehARqbNumymUT/ydwlLojFdv7Sk= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= +github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0= +github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.0.1-0.20190104013014-3767db7a7e18/go.mod h1:HD5P3vAIAh+Y2GAxg0PrPN1P8WkepXGpjbUPDHJqqKM= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cosmos/cosmos-sdk v0.38.3 h1:qIBTiw+2T9POaSUJ5rvbBbXeq8C8btBlJxnSegPBd3Y= +github.com/cosmos/cosmos-sdk v0.38.3/go.mod h1:rzWOofbKfRt3wxiylmYWEFHnxxGj0coyqgWl2I9obAw= +github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= +github.com/cosmos/ledger-cosmos-go v0.11.1/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY= +github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/danieljoos/wincred v1.0.2/go.mod h1:SnuYRW9lp1oJrZX/dXJqr0cPK5gYXqx3EJbmjhLdK9U= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= +github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/dop251/goja v0.0.0-20200219165308-d1232e640a87/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dvsekhvalnov/jose2go v0.0.0-20180829124132-7f401d37b68a/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/elastic/gosigar v0.8.1-0.20180330100440-37f05ff46ffa/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= +github.com/ethereum/go-ethereum v1.9.13 h1:rOPqjSngvs1VSYH2H+PMPiWt4VEulvNRbFgqiGqJM3E= +github.com/ethereum/go-ethereum v1.9.13/go.mod h1:qwN9d1GLyDh0N7Ab8bMGd0H9knaji2jOBm2RrMGjXls= +github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= +github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= +github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= +github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1-0.20190508161146-9fa652df1129/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2-0.20190517061210-b285ee9cfc6c/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= +github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= +github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/huin/goupnp v0.0.0-20161224104101-679507af18f3/go.mod h1:MZ2ZmwcBpvOoJ22IJsc7va19ZwoheaBk43rKg12SKag= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/karalabe/hid v1.0.0 h1:+/CIMNXhSU/zIJgnIvBD2nKHxS/bnRHhhs9xBryLpPo= +github.com/karalabe/hid v1.0.0/go.mod h1:Vr51f8rUOLYrfrWDFlV12GGQgM5AT8sVh+2fY4MPeu8= +github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= +github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +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/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= +github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= +github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= +github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.5.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rakyll/statik v0.1.6/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= +github.com/rjeczalik/notify v0.9.2 h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8= +github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shengdoushi/base58 v1.0.0 h1:tGe4o6TmdXFJWoI31VoSWvuaKxf0Px3gqa3sUWhAxBs= +github.com/shengdoushi/base58 v1.0.0/go.mod h1:m5uIILfzcKMw6238iWAhP4l3s5+uXyF3+bJKUNhAL9I= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa/go.mod h1:oJyF+mSPHbB5mVY2iO9KV3pTt/QbIkGaO8gQ2WrDbP4= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.0.1-0.20190317074736-539464a789e9/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +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/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= +github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= +github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw= +github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= +github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8= +github.com/tendermint/btcd v0.1.1/go.mod h1:DC6/m53jtQzr/NFmMNEu0rxf18/ktVoVtMrnDD5pN+U= +github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk= +github.com/tendermint/go-amino v0.14.1/go.mod h1:i/UKE5Uocn+argJJBb12qTZsCDBcAYMbR92AaJVmKso= +github.com/tendermint/go-amino v0.15.1/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= +github.com/tendermint/iavl v0.13.2/go.mod h1:vE1u0XAGXYjHykd4BLp8p/yivrw2PF1TuoljBcsQoGA= +github.com/tendermint/tendermint v0.33.2/go.mod h1:25DqB7YvV1tN3tHsjWoc2vFtlwICfrub9XO6UBO+4xk= +github.com/tendermint/tendermint v0.33.3/go.mod h1:25DqB7YvV1tN3tHsjWoc2vFtlwICfrub9XO6UBO+4xk= +github.com/tendermint/tm-db v0.4.1/go.mod h1:JsJ6qzYkCGiGwm5GHl/H5GLI9XLb6qZX7PRe425dHAY= +github.com/tendermint/tm-db v0.5.0/go.mod h1:lSq7q5WRR/njf1LnhiZ/lIJHk2S8Y1Zyq5oP/3o9C2U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= +github.com/tyler-smith/go-bip39 v1.0.2 h1:+t3w+KwLXO6154GNJY+qUtIxLTmFjfUmpguQT1OlOT8= +github.com/tyler-smith/go-bip39 v1.0.2/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= +go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88= +golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +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= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84 h1:pSLkPbrjnPyLDYUO2VM9mDLqo2V6CFBY84lFSZAfoi4= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= +gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200316214253-d7b0ff38cac9/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/main.go b/main.go deleted file mode 100644 index 71268a2d..00000000 --- a/main.go +++ /dev/null @@ -1,23 +0,0 @@ -package main - -import ( - "github.com/astaxie/beego" - "github.com/fbsobreira/gotron/common/global" - _ "github.com/fbsobreira/gotron/routers" - "github.com/fbsobreira/gotron/service" -) - -func main() { - if beego.BConfig.RunMode == "dev" { - beego.BConfig.WebConfig.DirectoryIndex = true - beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger" - } - - grpcAddress := beego.AppConfig.String("grpcaddress") - - global.TronClient = service.NewGrpcClient(grpcAddress) - global.TronClient.Start() - defer global.TronClient.Conn.Close() - - beego.Run() -} diff --git a/models/account.go b/models/account.go deleted file mode 100644 index 7c7b9dc9..00000000 --- a/models/account.go +++ /dev/null @@ -1,179 +0,0 @@ -package models - -import ( - "github.com/fbsobreira/gotron/common/base58" - "github.com/fbsobreira/gotron/common/global" - "github.com/fbsobreira/gotron/common/hexutil" -) - -type Account struct { - AccountName string - AccountType string - Address string - Balance int64 - Votes []*Vote - Asset map[string]int64 - AssetV2 map[string]int64 - Frozen []*Frozen - NetUsage int64 - AcquiredDelegatedFrozenBalanceForBandwidth int64 - DelegatedFrozenBalanceForBandwidth int64 - CreateTime int64 - LatestOprationTime int64 - Allowance int64 - LatestWithdrawTime int64 - Code string - IsWitness bool - IsCommittee bool - FrozenSupply []*Frozen - AssetIssuedName string - AssetIssuedID string - LatestAssetOperationTime map[string]int64 - LatestAssetOperationTimeV2 map[string]int64 - FreeNetUsage int64 - FreeAssetNetUsage map[string]int64 - FreeAssetNetUsageV2 map[string]int64 - LatestConsumeTime int64 - LatestConsumeFreeTime int64 - AccountID string - AccountResource *AccountResource -} - -type Vote struct { - VoteAddress string - VoteCount int64 -} - -type Frozen struct { - FrozenBalance int64 - ExpireTime int64 -} - -type AccountResource struct { - EnergyUsage int64 - FrozenBalanceForEnergy *Frozen - LatestConsumeTimeForEnergy int64 - AcquiredDelegatedFrozenBalanceForEnergy int64 - DelegatedFrozenBalanceForEnergy int64 - StorageLimit int64 - StorageUsage int64 - LatestExchangeStorageTime int64 -} - -func GetAccountByAddress(address string) (*Account, error) { - grpcAccount := global.TronClient.GetAccount(address) - - resultAccount := new(Account) - - resultAccount.AccountName = string(grpcAccount.AccountName) - resultAccount.AccountType = grpcAccount.Type.String() - resultAccount.Address = base58.EncodeCheck(grpcAccount.Address) - resultAccount.Balance = grpcAccount.Balance - - resultAccount.Votes = make([]*Vote, 0) - for _, v := range grpcAccount.Votes { - vote := new(Vote) - vote.VoteAddress = base58.EncodeCheck(v.VoteAddress) - vote.VoteCount = v.VoteCount - resultAccount.Votes = append(resultAccount.Votes, vote) - } - - resultAccount.Asset = make(map[string]int64) - for k, v := range grpcAccount.Asset { - resultAccount.Asset[k] = v - } - - resultAccount.AssetV2 = make(map[string]int64) - for k, v := range grpcAccount.AssetV2 { - resultAccount.AssetV2[k] = v - } - - resultAccount.Frozen = make([]*Frozen, 0) - for _, v := range grpcAccount.Frozen { - frozen := new(Frozen) - frozen.FrozenBalance = v.FrozenBalance - frozen.ExpireTime = v.ExpireTime - resultAccount.Frozen = append(resultAccount.Frozen, frozen) - } - - resultAccount.NetUsage = grpcAccount.NetUsage - resultAccount.AcquiredDelegatedFrozenBalanceForBandwidth = grpcAccount.AcquiredDelegatedFrozenBalanceForBandwidth - resultAccount.DelegatedFrozenBalanceForBandwidth = grpcAccount.DelegatedFrozenBalanceForBandwidth - resultAccount.CreateTime = grpcAccount.CreateTime - resultAccount.LatestOprationTime = grpcAccount.LatestOprationTime - resultAccount.Allowance = grpcAccount.Allowance - resultAccount.LatestWithdrawTime = grpcAccount.LatestWithdrawTime - resultAccount.Code = hexutil.Encode(grpcAccount.Code) - resultAccount.IsWitness = grpcAccount.IsWitness - resultAccount.IsCommittee = grpcAccount.IsCommittee - - resultAccount.FrozenSupply = make([]*Frozen, 0) - for _, v := range grpcAccount.FrozenSupply { - frozen := new(Frozen) - frozen.FrozenBalance = v.FrozenBalance - frozen.ExpireTime = v.ExpireTime - resultAccount.FrozenSupply = append(resultAccount.FrozenSupply, frozen) - } - - resultAccount.AssetIssuedName = hexutil.Encode(grpcAccount.AssetIssuedName) - resultAccount.AssetIssuedID = hexutil.Encode(grpcAccount.AssetIssued_ID) - - resultAccount.LatestAssetOperationTime = make(map[string]int64) - for k, v := range grpcAccount.LatestAssetOperationTime { - resultAccount.LatestAssetOperationTime[k] = v - } - - resultAccount.LatestAssetOperationTimeV2 = make(map[string]int64) - for k, v := range grpcAccount.LatestAssetOperationTimeV2 { - resultAccount.LatestAssetOperationTimeV2[k] = v - } - - resultAccount.FreeNetUsage = grpcAccount.FreeNetUsage - - resultAccount.FreeAssetNetUsage = make(map[string]int64) - for k, v := range grpcAccount.FreeAssetNetUsage { - resultAccount.FreeAssetNetUsage[k] = v - } - - resultAccount.FreeAssetNetUsageV2 = make(map[string]int64) - for k, v := range grpcAccount.FreeAssetNetUsageV2 { - resultAccount.FreeAssetNetUsageV2[k] = v - } - - resultAccount.LatestConsumeTime = grpcAccount.LatestConsumeTime - resultAccount.LatestConsumeFreeTime = grpcAccount.LatestConsumeFreeTime - resultAccount.AccountID = hexutil.Encode(grpcAccount.AccountId) - - // Account resource. - if grpcAccount.AccountResource != nil { - resultAccount.AccountResource = new(AccountResource) - resultAccount.AccountResource.EnergyUsage = grpcAccount.AccountResource.EnergyUsage - - if grpcAccount.AccountResource.FrozenBalanceForEnergy != nil { - resultAccount.AccountResource.FrozenBalanceForEnergy = new(Frozen) - resultAccount.AccountResource.FrozenBalanceForEnergy.FrozenBalance = - grpcAccount.AccountResource.FrozenBalanceForEnergy.FrozenBalance - - resultAccount.AccountResource.FrozenBalanceForEnergy.ExpireTime = - grpcAccount.AccountResource.FrozenBalanceForEnergy.ExpireTime - } - - resultAccount.AccountResource.LatestConsumeTimeForEnergy = - grpcAccount.AccountResource.LatestConsumeTimeForEnergy - - resultAccount.AccountResource.AcquiredDelegatedFrozenBalanceForEnergy = - grpcAccount.AccountResource.AcquiredDelegatedFrozenBalanceForEnergy - - resultAccount.AccountResource.DelegatedFrozenBalanceForEnergy = - grpcAccount.AccountResource.DelegatedFrozenBalanceForEnergy - - resultAccount.AccountResource.StorageLimit = grpcAccount.AccountResource.StorageLimit - - resultAccount.AccountResource.StorageUsage = grpcAccount.AccountResource.StorageUsage - - resultAccount.AccountResource.LatestExchangeStorageTime = - grpcAccount.AccountResource.LatestExchangeStorageTime - } - - return resultAccount, nil -} diff --git a/models/accountnetmessage.go b/models/accountnetmessage.go deleted file mode 100644 index 8139145a..00000000 --- a/models/accountnetmessage.go +++ /dev/null @@ -1,35 +0,0 @@ -package models - -import "github.com/fbsobreira/gotron/common/global" - -type AccountNetMessage struct { - FreeNetUsed int64 - FreeNetLimit int64 - NetUsed int64 - NetLimit int64 - AssetNetUsed map[string]int64 - AssetNetLimit map[string]int64 - TotalNetLimit int64 - TotalNetWeight int64 -} - -func GetAccountNet(address string) AccountNetMessage { - grpcAccountNet := global.TronClient.GetAccountNet(address) - - var resultAccountNet AccountNetMessage - - if grpcAccountNet == nil { - return resultAccountNet - } - - resultAccountNet.FreeNetUsed = grpcAccountNet.FreeNetUsed - resultAccountNet.FreeNetLimit = grpcAccountNet.FreeNetLimit - resultAccountNet.NetUsed = grpcAccountNet.NetUsed - resultAccountNet.NetLimit = grpcAccountNet.NetLimit - resultAccountNet.AssetNetUsed = grpcAccountNet.AssetNetUsed - resultAccountNet.AssetNetLimit = grpcAccountNet.AssetNetLimit - resultAccountNet.TotalNetLimit = grpcAccountNet.TotalNetLimit - resultAccountNet.TotalNetWeight = grpcAccountNet.TotalNetWeight - - return resultAccountNet -} diff --git a/models/assetissue.go b/models/assetissue.go deleted file mode 100644 index 66bea1fd..00000000 --- a/models/assetissue.go +++ /dev/null @@ -1,162 +0,0 @@ -package models - -import ( - "github.com/fbsobreira/gotron/common/base58" - "github.com/fbsobreira/gotron/common/global" -) - -type AssetIssueList struct { - AssetIssue []AssetIssueContract -} - -type AssetIssueContract struct { - OwnerAddress string - Name string - Abbr string - TotalSupply int64 - FrozenSupply []FrozenSupply - TrxNum int32 - Num int32 - StartTime int64 - EndTime int64 - VoteScore int32 - Description string - Url string - FreeAssetNetLimit int64 - PublicFreeAssetNetLimit int64 - PublicFreeAssetNetUsage int64 - PublicLatestFreeNetTime int64 -} - -type FrozenSupply struct { - FrozenAmount int64 - FrozenDays int64 -} - -func GetAssetIssueAccount(address string) AssetIssueList { - grpcAssetIssueList := global.TronClient.GetAssetIssueByAccount(address) - - var resultAssetIssueList AssetIssueList - - if grpcAssetIssueList == nil { - return resultAssetIssueList - } - - resultAssetIssueList.AssetIssue = make([]AssetIssueContract, 0) - for _, a := range grpcAssetIssueList.AssetIssue { - var assetIssueContract AssetIssueContract - assetIssueContract.OwnerAddress = base58.EncodeCheck(a.OwnerAddress) - assetIssueContract.Name = string(a.Name) - assetIssueContract.Abbr = string(a.Abbr) - assetIssueContract.TotalSupply = a.TotalSupply - - assetIssueContract.FrozenSupply = make([]FrozenSupply, 0) - for _, f := range a.FrozenSupply { - var frozenSupply FrozenSupply - frozenSupply.FrozenAmount = f.FrozenAmount - frozenSupply.FrozenDays = f.FrozenDays - assetIssueContract.FrozenSupply = append(assetIssueContract. - FrozenSupply, frozenSupply) - } - - assetIssueContract.TrxNum = a.TrxNum - assetIssueContract.Num = a.Num - assetIssueContract.StartTime = a.StartTime - assetIssueContract.EndTime = a.EndTime - assetIssueContract.VoteScore = a.VoteScore - assetIssueContract.Description = string(a.Description) - assetIssueContract.Url = string(a.Url) - assetIssueContract.FreeAssetNetLimit = a.FreeAssetNetLimit - assetIssueContract.PublicFreeAssetNetLimit = a.PublicFreeAssetNetLimit - assetIssueContract.PublicFreeAssetNetUsage = a.PublicFreeAssetNetUsage - assetIssueContract.PublicLatestFreeNetTime = a.PublicLatestFreeNetTime - - resultAssetIssueList.AssetIssue = append(resultAssetIssueList. - AssetIssue, assetIssueContract) - } - - return resultAssetIssueList -} - -func GetAssetIssueByName(name string) AssetIssueContract { - grpcAssetIssue := global.TronClient.GetAssetIssueByName(name) - - var assetIssueContract AssetIssueContract - - if grpcAssetIssue == nil { - return assetIssueContract - } - - assetIssueContract.OwnerAddress = base58.EncodeCheck(grpcAssetIssue.OwnerAddress) - assetIssueContract.Name = string(grpcAssetIssue.Name) - assetIssueContract.Abbr = string(grpcAssetIssue.Abbr) - assetIssueContract.TotalSupply = grpcAssetIssue.TotalSupply - - assetIssueContract.FrozenSupply = make([]FrozenSupply, 0) - for _, f := range grpcAssetIssue.FrozenSupply { - var frozenSupply FrozenSupply - frozenSupply.FrozenAmount = f.FrozenAmount - frozenSupply.FrozenDays = f.FrozenDays - assetIssueContract.FrozenSupply = append(assetIssueContract. - FrozenSupply, frozenSupply) - } - - assetIssueContract.TrxNum = grpcAssetIssue.TrxNum - assetIssueContract.Num = grpcAssetIssue.Num - assetIssueContract.StartTime = grpcAssetIssue.StartTime - assetIssueContract.EndTime = grpcAssetIssue.EndTime - assetIssueContract.VoteScore = grpcAssetIssue.VoteScore - assetIssueContract.Description = string(grpcAssetIssue.Description) - assetIssueContract.Url = string(grpcAssetIssue.Url) - assetIssueContract.FreeAssetNetLimit = grpcAssetIssue.FreeAssetNetLimit - assetIssueContract.PublicFreeAssetNetLimit = grpcAssetIssue.PublicFreeAssetNetLimit - assetIssueContract.PublicFreeAssetNetUsage = grpcAssetIssue.PublicFreeAssetNetUsage - assetIssueContract.PublicLatestFreeNetTime = grpcAssetIssue.PublicLatestFreeNetTime - - return assetIssueContract -} - -func GetAssetIssueList() AssetIssueList { - grpcAssetIssueList := global.TronClient.GetAssetIssueList() - - var resultAssetIssueList AssetIssueList - - if grpcAssetIssueList == nil { - return resultAssetIssueList - } - - resultAssetIssueList.AssetIssue = make([]AssetIssueContract, 0) - for _, a := range grpcAssetIssueList.AssetIssue { - var assetIssueContract AssetIssueContract - assetIssueContract.OwnerAddress = base58.EncodeCheck(a.OwnerAddress) - assetIssueContract.Name = string(a.Name) - assetIssueContract.Abbr = string(a.Abbr) - assetIssueContract.TotalSupply = a.TotalSupply - - assetIssueContract.FrozenSupply = make([]FrozenSupply, 0) - for _, f := range a.FrozenSupply { - var frozenSupply FrozenSupply - frozenSupply.FrozenAmount = f.FrozenAmount - frozenSupply.FrozenDays = f.FrozenDays - assetIssueContract.FrozenSupply = append(assetIssueContract. - FrozenSupply, frozenSupply) - } - - assetIssueContract.TrxNum = a.TrxNum - assetIssueContract.Num = a.Num - assetIssueContract.StartTime = a.StartTime - assetIssueContract.EndTime = a.EndTime - assetIssueContract.VoteScore = a.VoteScore - assetIssueContract.Description = string(a.Description) - assetIssueContract.Url = string(a.Url) - assetIssueContract.FreeAssetNetLimit = a.FreeAssetNetLimit - assetIssueContract.PublicFreeAssetNetLimit = a.PublicFreeAssetNetLimit - assetIssueContract.PublicFreeAssetNetUsage = a.PublicFreeAssetNetUsage - assetIssueContract.PublicLatestFreeNetTime = a.PublicLatestFreeNetTime - - resultAssetIssueList.AssetIssue = append(resultAssetIssueList. - AssetIssue, assetIssueContract) - } - - return resultAssetIssueList -} diff --git a/models/block.go b/models/block.go deleted file mode 100644 index 28f2c290..00000000 --- a/models/block.go +++ /dev/null @@ -1,579 +0,0 @@ -package models - -import ( - "github.com/fbsobreira/gotron/common/base58" - "github.com/fbsobreira/gotron/common/global" - "github.com/fbsobreira/gotron/common/hexutil" -) - -type BlockList struct { - Block []*Block -} - -type Block struct { - Transactions []*Transaction - BlockHeader *BlockHeader -} - -type BlockHeader struct { - RawData *BlockHeaderRaw - WitnessSignature string -} - -type BlockHeaderRaw struct { - Timestamp int64 - TxTrieRoot string - ParentHash string - Number int64 - WitnessId int64 - WitnessAddress string -} - -func GetNowBlock() Block { - var nowBlock Block - - grpcNowBlock := global.TronClient.GetNowBlock() - - nowBlock.Transactions = make([]*Transaction, 0) - - for _, t := range grpcNowBlock.Transactions { - transaction := new(Transaction) - - if t.RawData != nil { - transaction.RawData.RefBlockBytes = hexutil.Encode(t.RawData.RefBlockBytes) - transaction.RawData.RefBlockNum = t.RawData.RefBlockNum - transaction.RawData.RefBlockHash = hexutil.Encode(t.RawData.RefBlockHash) - transaction.RawData.Expiration = t.RawData.Expiration - - transaction.RawData.Auths = make([]*Acuthrity, 0) - for _, a := range t.RawData.Auths { - auth := new(Acuthrity) - - accountId := new(AccountId) - accountId.Name = string(a.Account.Name) - accountId.Address = base58.EncodeCheck(a.Account.Address) - - auth.Account = accountId - - auth.PermissionName = string(a.PermissionName) - - transaction.RawData.Auths = append(transaction.RawData.Auths, - auth) - } - - transaction.RawData.Data = string(t.RawData.Data) - - transaction.RawData.Contract = make([]*Contract, 0) - for _, c := range t.RawData.Contract { - contract := new(Contract) - contract.Type = c.Type.String() - contract.Parameter = c.Parameter - contract.Provider = string(c.Provider) - contract.ContractName = string(c.ContractName) - - transaction.RawData.Contract = append(transaction.RawData. - Contract, contract) - } - - transaction.RawData.Scripts = string(t.RawData.Scripts) - transaction.RawData.Timestamp = t.RawData.Timestamp - } - - transaction.Signature = make([]string, 0) - for _, s := range t.Signature { - transaction.Signature = append(transaction.Signature, hexutil.Encode(s)) - } - - transaction.Ret = make([]*Result, 0) - for _, r := range t.Ret { - result := new(Result) - result.Ret = string(r.Ret) - result.Fee = r.Fee - transaction.Ret = append(transaction.Ret, result) - } - - nowBlock.Transactions = append(nowBlock.Transactions, transaction) - } - - if grpcNowBlock.BlockHeader != nil { - if grpcNowBlock.BlockHeader.RawData != nil { - nowBlock.BlockHeader.RawData.Timestamp = grpcNowBlock. - BlockHeader.RawData.Timestamp - - nowBlock.BlockHeader.RawData.TxTrieRoot = hexutil.Encode(grpcNowBlock. - BlockHeader.RawData.TxTrieRoot) - - nowBlock.BlockHeader.RawData.ParentHash = hexutil.Encode(grpcNowBlock. - BlockHeader.RawData.ParentHash) - - nowBlock.BlockHeader.RawData.Number = grpcNowBlock. - BlockHeader.RawData.Number - - nowBlock.BlockHeader.RawData.WitnessId = grpcNowBlock. - BlockHeader.RawData.WitnessId - - nowBlock.BlockHeader.RawData.WitnessAddress = base58.EncodeCheck(grpcNowBlock. - BlockHeader.RawData.WitnessAddress) - } - - nowBlock.BlockHeader.WitnessSignature = hexutil.Encode(grpcNowBlock. - BlockHeader.WitnessSignature) - } - - return nowBlock -} - -func GetBlockByNum(num int64) Block { - grpcBlock := global.TronClient.GetBlockByNum(num) - - var block Block - - if grpcBlock == nil { - return block - } - - block.Transactions = make([]*Transaction, 0) - - for _, t := range grpcBlock.Transactions { - transaction := new(Transaction) - - if t.RawData != nil { - transaction.RawData.RefBlockBytes = hexutil.Encode(t.RawData.RefBlockBytes) - transaction.RawData.RefBlockNum = t.RawData.RefBlockNum - transaction.RawData.RefBlockHash = hexutil.Encode(t.RawData.RefBlockHash) - transaction.RawData.Expiration = t.RawData.Expiration - - transaction.RawData.Auths = make([]*Acuthrity, 0) - for _, a := range t.RawData.Auths { - auth := new(Acuthrity) - - accountId := new(AccountId) - accountId.Name = string(a.Account.Name) - accountId.Address = base58.EncodeCheck(a.Account.Address) - - auth.Account = accountId - - auth.PermissionName = string(a.PermissionName) - - transaction.RawData.Auths = append(transaction.RawData.Auths, - auth) - } - - transaction.RawData.Data = string(t.RawData.Data) - - transaction.RawData.Contract = make([]*Contract, 0) - for _, c := range t.RawData.Contract { - contract := new(Contract) - contract.Type = c.Type.String() - contract.Parameter = c.Parameter - contract.Provider = string(c.Provider) - contract.ContractName = string(c.ContractName) - - transaction.RawData.Contract = append(transaction.RawData. - Contract, contract) - } - - transaction.RawData.Scripts = string(t.RawData.Scripts) - transaction.RawData.Timestamp = t.RawData.Timestamp - } - - transaction.Signature = make([]string, 0) - for _, s := range t.Signature { - transaction.Signature = append(transaction.Signature, hexutil.Encode(s)) - } - - transaction.Ret = make([]*Result, 0) - for _, r := range t.Ret { - result := new(Result) - result.Ret = string(r.Ret) - result.Fee = r.Fee - transaction.Ret = append(transaction.Ret, result) - } - - block.Transactions = append(block.Transactions, transaction) - } - - if grpcBlock.BlockHeader != nil { - if grpcBlock.BlockHeader.RawData != nil { - block.BlockHeader.RawData.Timestamp = grpcBlock. - BlockHeader.RawData.Timestamp - - block.BlockHeader.RawData.TxTrieRoot = hexutil.Encode(grpcBlock. - BlockHeader.RawData.TxTrieRoot) - - block.BlockHeader.RawData.ParentHash = hexutil.Encode(grpcBlock. - BlockHeader.RawData.ParentHash) - - block.BlockHeader.RawData.Number = grpcBlock. - BlockHeader.RawData.Number - - block.BlockHeader.RawData.WitnessId = grpcBlock. - BlockHeader.RawData.WitnessId - - block.BlockHeader.RawData.WitnessAddress = base58.EncodeCheck(grpcBlock. - BlockHeader.RawData.WitnessAddress) - } - - block.BlockHeader.WitnessSignature = hexutil.Encode(grpcBlock. - BlockHeader.WitnessSignature) - } - - return block -} - -func GetBlockById(id string) Block { - grpcBlock := global.TronClient.GetBlockById(id) - - var block Block - - if grpcBlock == nil { - return block - } - - block.Transactions = make([]*Transaction, 0) - - for _, t := range grpcBlock.Transactions { - transaction := new(Transaction) - - if t.RawData != nil { - transaction.RawData.RefBlockBytes = hexutil.Encode(t.RawData.RefBlockBytes) - transaction.RawData.RefBlockNum = t.RawData.RefBlockNum - transaction.RawData.RefBlockHash = hexutil.Encode(t.RawData.RefBlockHash) - transaction.RawData.Expiration = t.RawData.Expiration - - transaction.RawData.Auths = make([]*Acuthrity, 0) - for _, a := range t.RawData.Auths { - auth := new(Acuthrity) - - accountId := new(AccountId) - accountId.Name = string(a.Account.Name) - accountId.Address = base58.EncodeCheck(a.Account.Address) - - auth.Account = accountId - - auth.PermissionName = string(a.PermissionName) - - transaction.RawData.Auths = append(transaction.RawData.Auths, - auth) - } - - transaction.RawData.Data = string(t.RawData.Data) - - transaction.RawData.Contract = make([]*Contract, 0) - for _, c := range t.RawData.Contract { - contract := new(Contract) - contract.Type = c.Type.String() - contract.Parameter = c.Parameter - contract.Provider = string(c.Provider) - contract.ContractName = string(c.ContractName) - - transaction.RawData.Contract = append(transaction.RawData. - Contract, contract) - } - - transaction.RawData.Scripts = string(t.RawData.Scripts) - transaction.RawData.Timestamp = t.RawData.Timestamp - } - - transaction.Signature = make([]string, 0) - for _, s := range t.Signature { - transaction.Signature = append(transaction.Signature, hexutil.Encode(s)) - } - - transaction.Ret = make([]*Result, 0) - for _, r := range t.Ret { - result := new(Result) - result.Ret = string(r.Ret) - result.Fee = r.Fee - transaction.Ret = append(transaction.Ret, result) - } - - block.Transactions = append(block.Transactions, transaction) - } - - if grpcBlock.BlockHeader != nil { - if grpcBlock.BlockHeader.RawData != nil { - block.BlockHeader.RawData.Timestamp = grpcBlock. - BlockHeader.RawData.Timestamp - - block.BlockHeader.RawData.TxTrieRoot = hexutil.Encode(grpcBlock. - BlockHeader.RawData.TxTrieRoot) - - block.BlockHeader.RawData.ParentHash = hexutil.Encode(grpcBlock. - BlockHeader.RawData.ParentHash) - - block.BlockHeader.RawData.Number = grpcBlock. - BlockHeader.RawData.Number - - block.BlockHeader.RawData.WitnessId = grpcBlock. - BlockHeader.RawData.WitnessId - - block.BlockHeader.RawData.WitnessAddress = base58.EncodeCheck(grpcBlock. - BlockHeader.RawData.WitnessAddress) - } - - block.BlockHeader.WitnessSignature = hexutil.Encode(grpcBlock. - BlockHeader.WitnessSignature) - } - - return block -} - -func GetBlockByLimitNext(start, end int64) BlockList { - grpcBlockList := global.TronClient.GetBlockByLimitNext(start, end) - - var blockList BlockList - - for _, b := range grpcBlockList.Block { - block := new(Block) - block.Transactions = make([]*Transaction, 0) - - for _, t := range b.Transactions { - transaction := new(Transaction) - - if t.RawData != nil { - transaction.RawData.RefBlockBytes = hexutil.Encode(t.RawData.RefBlockBytes) - transaction.RawData.RefBlockNum = t.RawData.RefBlockNum - transaction.RawData.RefBlockHash = hexutil.Encode(t.RawData.RefBlockHash) - transaction.RawData.Expiration = t.RawData.Expiration - - transaction.RawData.Auths = make([]*Acuthrity, 0) - for _, a := range t.RawData.Auths { - auth := new(Acuthrity) - - accountId := new(AccountId) - accountId.Name = string(a.Account.Name) - accountId.Address = base58.EncodeCheck(a.Account.Address) - - auth.Account = accountId - - auth.PermissionName = string(a.PermissionName) - - transaction.RawData.Auths = append(transaction.RawData.Auths, - auth) - } - - transaction.RawData.Data = string(t.RawData.Data) - - transaction.RawData.Contract = make([]*Contract, 0) - for _, c := range t.RawData.Contract { - contract := new(Contract) - contract.Type = c.Type.String() - contract.Parameter = c.Parameter - contract.Provider = string(c.Provider) - contract.ContractName = string(c.ContractName) - - transaction.RawData.Contract = append(transaction.RawData. - Contract, contract) - } - - transaction.RawData.Scripts = string(t.RawData.Scripts) - transaction.RawData.Timestamp = t.RawData.Timestamp - } - - transaction.Signature = make([]string, 0) - for _, s := range t.Signature { - transaction.Signature = append(transaction.Signature, hexutil.Encode(s)) - } - - transaction.Ret = make([]*Result, 0) - for _, r := range t.Ret { - result := new(Result) - result.Ret = string(r.Ret) - result.Fee = r.Fee - transaction.Ret = append(transaction.Ret, result) - } - - block.Transactions = append(block.Transactions, transaction) - } - - if b.BlockHeader != nil { - if b.BlockHeader.RawData != nil { - block.BlockHeader.RawData.Timestamp = b. - BlockHeader.RawData.Timestamp - - block.BlockHeader.RawData.TxTrieRoot = hexutil.Encode(b. - BlockHeader.RawData.TxTrieRoot) - - block.BlockHeader.RawData.ParentHash = hexutil.Encode(b. - BlockHeader.RawData.ParentHash) - - block.BlockHeader.RawData.Number = b. - BlockHeader.RawData.Number - - block.BlockHeader.RawData.WitnessId = b. - BlockHeader.RawData.WitnessId - - block.BlockHeader.RawData.WitnessAddress = base58.EncodeCheck(b. - BlockHeader.RawData.WitnessAddress) - } - - block.BlockHeader.WitnessSignature = hexutil.Encode(b. - BlockHeader.WitnessSignature) - } - - blockList.Block = append(blockList.Block, block) - } - - return blockList -} - -func GetTransactionById(id string) Transaction { - grpcTransaction := global.TronClient.GetTransactionById(id) - - var resultTransaction Transaction - - if grpcTransaction.RawData != nil { - resultTransaction.RawData.RefBlockBytes = hexutil.Encode(grpcTransaction.RawData.RefBlockBytes) - resultTransaction.RawData.RefBlockNum = grpcTransaction.RawData.RefBlockNum - resultTransaction.RawData.RefBlockHash = hexutil.Encode(grpcTransaction.RawData.RefBlockHash) - resultTransaction.RawData.Expiration = grpcTransaction.RawData.Expiration - - resultTransaction.RawData.Auths = make([]*Acuthrity, 0) - for _, a := range grpcTransaction.RawData.Auths { - auth := new(Acuthrity) - - accountId := new(AccountId) - accountId.Name = string(a.Account.Name) - accountId.Address = base58.EncodeCheck(a.Account.Address) - - auth.Account = accountId - - auth.PermissionName = string(a.PermissionName) - - resultTransaction.RawData.Auths = append(resultTransaction.RawData.Auths, - auth) - } - - resultTransaction.RawData.Data = string(grpcTransaction.RawData.Data) - - resultTransaction.RawData.Contract = make([]*Contract, 0) - for _, c := range grpcTransaction.RawData.Contract { - contract := new(Contract) - contract.Type = c.Type.String() - contract.Parameter = c.Parameter - contract.Provider = string(c.Provider) - contract.ContractName = string(c.ContractName) - - resultTransaction.RawData.Contract = append(resultTransaction.RawData. - Contract, contract) - } - - resultTransaction.RawData.Scripts = string(grpcTransaction.RawData.Scripts) - resultTransaction.RawData.Timestamp = grpcTransaction.RawData.Timestamp - } - - resultTransaction.Signature = make([]string, 0) - for _, s := range grpcTransaction.Signature { - resultTransaction.Signature = append(resultTransaction.Signature, hexutil.Encode(s)) - } - - resultTransaction.Ret = make([]*Result, 0) - for _, r := range grpcTransaction.Ret { - result := new(Result) - result.Ret = string(r.Ret) - result.Fee = r.Fee - resultTransaction.Ret = append(resultTransaction.Ret, result) - } - - return resultTransaction -} - -func GetBlockByLatestNum(num int64) BlockList { - grpcBlockList := global.TronClient.GetBlockByLatestNum(num) - - var blockList BlockList - - for _, b := range grpcBlockList.Block { - block := new(Block) - block.Transactions = make([]*Transaction, 0) - - for _, t := range b.Transactions { - transaction := new(Transaction) - - if t.RawData != nil { - transaction.RawData.RefBlockBytes = hexutil.Encode(t.RawData.RefBlockBytes) - transaction.RawData.RefBlockNum = t.RawData.RefBlockNum - transaction.RawData.RefBlockHash = hexutil.Encode(t.RawData.RefBlockHash) - transaction.RawData.Expiration = t.RawData.Expiration - - transaction.RawData.Auths = make([]*Acuthrity, 0) - for _, a := range t.RawData.Auths { - auth := new(Acuthrity) - - accountId := new(AccountId) - accountId.Name = string(a.Account.Name) - accountId.Address = base58.EncodeCheck(a.Account.Address) - - auth.Account = accountId - - auth.PermissionName = string(a.PermissionName) - - transaction.RawData.Auths = append(transaction.RawData.Auths, - auth) - } - - transaction.RawData.Data = string(t.RawData.Data) - - transaction.RawData.Contract = make([]*Contract, 0) - for _, c := range t.RawData.Contract { - contract := new(Contract) - contract.Type = c.Type.String() - contract.Parameter = c.Parameter - contract.Provider = string(c.Provider) - contract.ContractName = string(c.ContractName) - - transaction.RawData.Contract = append(transaction.RawData. - Contract, contract) - } - - transaction.RawData.Scripts = string(t.RawData.Scripts) - transaction.RawData.Timestamp = t.RawData.Timestamp - } - - transaction.Signature = make([]string, 0) - for _, s := range t.Signature { - transaction.Signature = append(transaction.Signature, hexutil.Encode(s)) - } - - transaction.Ret = make([]*Result, 0) - for _, r := range t.Ret { - result := new(Result) - result.Ret = string(r.Ret) - result.Fee = r.Fee - transaction.Ret = append(transaction.Ret, result) - } - - block.Transactions = append(block.Transactions, transaction) - } - - if b.BlockHeader != nil { - if b.BlockHeader.RawData != nil { - block.BlockHeader.RawData.Timestamp = b. - BlockHeader.RawData.Timestamp - - block.BlockHeader.RawData.TxTrieRoot = hexutil.Encode(b. - BlockHeader.RawData.TxTrieRoot) - - block.BlockHeader.RawData.ParentHash = hexutil.Encode(b. - BlockHeader.RawData.ParentHash) - - block.BlockHeader.RawData.Number = b. - BlockHeader.RawData.Number - - block.BlockHeader.RawData.WitnessId = b. - BlockHeader.RawData.WitnessId - - block.BlockHeader.RawData.WitnessAddress = base58.EncodeCheck(b. - BlockHeader.RawData.WitnessAddress) - } - - block.BlockHeader.WitnessSignature = hexutil.Encode(b. - BlockHeader.WitnessSignature) - } - - blockList.Block = append(blockList.Block, block) - } - - return blockList -} diff --git a/models/contract/accountcreatecontract.go b/models/contract/accountcreatecontract.go deleted file mode 100644 index 1375410c..00000000 --- a/models/contract/accountcreatecontract.go +++ /dev/null @@ -1,24 +0,0 @@ -package contract - -import ( - "github.com/fbsobreira/gotron/common/base58" - "github.com/fbsobreira/gotron/common/global" - "github.com/fbsobreira/gotron/core" -) - -type AccountCreateContract struct { - OwnerAddress string `json:"ownerAddress"` - AccountAddress string `json:"accountAddress"` - Type core.AccountType `json:"type"` -} - -func CreateAccount(contract AccountCreateContract) (*core.Transaction, - error) { - - grpcContract := new(core.AccountCreateContract) - - grpcContract.OwnerAddress = base58.DecodeCheck(contract.OwnerAddress) - grpcContract.AccountAddress = base58.DecodeCheck(contract.AccountAddress) - - return global.TronClient.CreateAccountByContract(grpcContract) -} diff --git a/models/node.go b/models/node.go deleted file mode 100644 index bdc45a86..00000000 --- a/models/node.go +++ /dev/null @@ -1,45 +0,0 @@ -package models - -import ( - "github.com/fbsobreira/gotron/common/global" -) - -type NodeList struct { - Nodes []Node -} - -type Node struct { - Address Address -} - -type Address struct { - Host string - Port int32 -} - -func GetNodeList() NodeList { - var nodes NodeList - nodes.Nodes = make([]Node, 0) - - grpcNodes := global.TronClient.ListNodes() - - if grpcNodes == nil { - return nodes - } - - for _, n := range grpcNodes.Nodes { - var node Node - var address Address - - if n.Address != nil { - address.Host = string(n.Address.Host) - address.Port = n.Address.Port - } - - node.Address = address - - nodes.Nodes = append(nodes.Nodes, node) - } - - return nodes -} diff --git a/models/numbermessage.go b/models/numbermessage.go deleted file mode 100644 index 3f6b13aa..00000000 --- a/models/numbermessage.go +++ /dev/null @@ -1,25 +0,0 @@ -package models - -import "github.com/fbsobreira/gotron/common/global" - -type NumberMessage struct { - Num int64 -} - -func GetNextMaintenanceTime() NumberMessage { - grpcNextMaintenanceTime := global.TronClient.GetNextMaintenanceTime() - - var resultNextMaintenanceTime NumberMessage - resultNextMaintenanceTime.Num = grpcNextMaintenanceTime.Num - - return resultNextMaintenanceTime -} - -func GetTotalTransaction() NumberMessage { - grpcTotalTransaction := global.TronClient.TotalTransaction() - - var resultTotalTransaction NumberMessage - resultTotalTransaction.Num = grpcTotalTransaction.Num - - return resultTotalTransaction -} diff --git a/models/object.go b/models/object.go deleted file mode 100644 index 2c72e6b6..00000000 --- a/models/object.go +++ /dev/null @@ -1,52 +0,0 @@ -package models - -import ( - "errors" - "strconv" - "time" -) - -var ( - Objects map[string]*Object -) - -type Object struct { - ObjectId string - Score int64 - PlayerName string -} - -func init() { - Objects = make(map[string]*Object) - Objects["hjkhsbnmn123"] = &Object{"hjkhsbnmn123", 100, "astaxie"} - Objects["mjjkxsxsaa23"] = &Object{"mjjkxsxsaa23", 101, "someone"} -} - -func AddOne(object Object) (ObjectId string) { - object.ObjectId = "astaxie" + strconv.FormatInt(time.Now().UnixNano(), 10) - Objects[object.ObjectId] = &object - return object.ObjectId -} - -func GetOne(ObjectId string) (object *Object, err error) { - if v, ok := Objects[ObjectId]; ok { - return v, nil - } - return nil, errors.New("ObjectId Not Exist") -} - -func GetAll() map[string]*Object { - return Objects -} - -func Update(ObjectId string, Score int64) (err error) { - if v, ok := Objects[ObjectId]; ok { - v.Score = Score - return nil - } - return errors.New("ObjectId Not Exist") -} - -func Delete(ObjectId string) { - delete(Objects, ObjectId) -} diff --git a/models/transaction.go b/models/transaction.go deleted file mode 100644 index 28784043..00000000 --- a/models/transaction.go +++ /dev/null @@ -1,41 +0,0 @@ -package models - -type Transaction struct { - RawData *TransactionRaw - Signature []string - Ret []*Result -} - -type TransactionRaw struct { - RefBlockBytes string - RefBlockNum int64 - RefBlockHash string - Expiration int64 - Auths []*Acuthrity - Data string - Contract []*Contract - Scripts string - Timestamp int64 -} - -type Acuthrity struct { - Account *AccountId - PermissionName string -} - -type AccountId struct { - Name string - Address string -} - -type Contract struct { - Type string - Parameter interface{} - Provider string - ContractName string -} - -type Result struct { - Fee int64 - Ret string -} diff --git a/models/user.go b/models/user.go deleted file mode 100644 index d4bebb20..00000000 --- a/models/user.go +++ /dev/null @@ -1,86 +0,0 @@ -package models - -import ( - "errors" - "strconv" - "time" -) - -var ( - UserList map[string]*User -) - -func init() { - UserList = make(map[string]*User) - u := User{"user_11111", "astaxie", "11111", Profile{"male", 20, "Singapore", "astaxie@gmail.com"}} - UserList["user_11111"] = &u -} - -type User struct { - Id string - Username string - Password string - Profile Profile -} - -type Profile struct { - Gender string - Age int - Address string - Email string -} - -func AddUser(u User) string { - u.Id = "user_" + strconv.FormatInt(time.Now().UnixNano(), 10) - UserList[u.Id] = &u - return u.Id -} - -func GetUser(uid string) (u *User, err error) { - if u, ok := UserList[uid]; ok { - return u, nil - } - return nil, errors.New("User not exists") -} - -func GetAllUsers() map[string]*User { - return UserList -} - -func UpdateUser(uid string, uu *User) (a *User, err error) { - if u, ok := UserList[uid]; ok { - if uu.Username != "" { - u.Username = uu.Username - } - if uu.Password != "" { - u.Password = uu.Password - } - if uu.Profile.Age != 0 { - u.Profile.Age = uu.Profile.Age - } - if uu.Profile.Address != "" { - u.Profile.Address = uu.Profile.Address - } - if uu.Profile.Gender != "" { - u.Profile.Gender = uu.Profile.Gender - } - if uu.Profile.Email != "" { - u.Profile.Email = uu.Profile.Email - } - return u, nil - } - return nil, errors.New("User Not Exist") -} - -func Login(username, password string) bool { - for _, u := range UserList { - if u.Username == username && u.Password == password { - return true - } - } - return false -} - -func DeleteUser(uid string) { - delete(UserList, uid) -} diff --git a/models/witness.go b/models/witness.go deleted file mode 100644 index b47d73d4..00000000 --- a/models/witness.go +++ /dev/null @@ -1,50 +0,0 @@ -package models - -import ( - "github.com/fbsobreira/gotron/common/base58" - "github.com/fbsobreira/gotron/common/global" - "github.com/fbsobreira/gotron/common/hexutil" -) - -type WitnessList struct { - Witnesses []Witness -} - -type Witness struct { - Address string - VoteCount int64 - PubKey string - Url string - TotalProduced int64 - TotalMissed int64 - LatestBlockNum int64 - LatestSlotNum int64 - IsJobs bool -} - -func GetWitnessList() WitnessList { - var witnesses WitnessList - witnesses.Witnesses = make([]Witness, 0) - - grpcWitnesses := global.TronClient.ListWitnesses() - - if grpcWitnesses == nil { - return witnesses - } - - for _, w := range grpcWitnesses.Witnesses { - var witness Witness - witness.Address = base58.EncodeCheck(w.Address) - witness.VoteCount = w.VoteCount - witness.PubKey = hexutil.Encode(w.PubKey) - witness.Url = w.Url - witness.TotalProduced = w.TotalProduced - witness.TotalMissed = w.TotalMissed - witness.LatestBlockNum = w.LatestBlockNum - witness.LatestSlotNum = w.LatestSlotNum - witness.IsJobs = w.IsJobs - witnesses.Witnesses = append(witnesses.Witnesses, witness) - } - - return witnesses -} diff --git a/pkg/account/creation.go b/pkg/account/creation.go new file mode 100644 index 00000000..e74b7aaf --- /dev/null +++ b/pkg/account/creation.go @@ -0,0 +1,43 @@ +package account + +import ( + "github.com/fbsobreira/gotron-sdk/pkg/keys" + "github.com/fbsobreira/gotron-sdk/pkg/mnemonic" + "github.com/fbsobreira/gotron-sdk/pkg/store" +) + +// Creation struct for account +type Creation struct { + Name string + Passphrase string + Mnemonic string + MnemonicPassphrase string + HdAccountNumber *uint32 + HdIndexNumber *uint32 +} + +// New create new name +func New() string { + return "New Account" +} + +// IsValidPassphrase check if strong +func IsValidPassphrase(pass string) bool { + // TODO: force strong password + return true +} + +// CreateNewLocalAccount assumes all the inputs are valid, legitmate +func CreateNewLocalAccount(candidate *Creation) error { + ks := store.FromAccountName(candidate.Name) + if candidate.Mnemonic == "" { + candidate.Mnemonic = mnemonic.Generate() + } + // Hardcoded index of 0 for brandnew account. + private, _ := keys.FromMnemonicSeedAndPassphrase(candidate.Mnemonic, candidate.MnemonicPassphrase, 0) + _, err := ks.ImportECDSA(private.ToECDSA(), candidate.Passphrase) + if err != nil { + return err + } + return nil +} diff --git a/pkg/account/export.go b/pkg/account/export.go new file mode 100644 index 00000000..fdb621e5 --- /dev/null +++ b/pkg/account/export.go @@ -0,0 +1,45 @@ +package account + +import ( + "fmt" + "path/filepath" + + "github.com/fbsobreira/gotron-sdk/pkg/keystore" + "github.com/fbsobreira/gotron-sdk/pkg/store" +) + +// ExportPrivateKey from account +func ExportPrivateKey(address, passphrase string) error { + ks := store.FromAddress(address) + allAccounts := ks.Accounts() + for _, account := range allAccounts { + _, key, err := ks.GetDecryptedKey(keystore.Account{Address: account.Address}, passphrase) + if err != nil { + return err + } + fmt.Printf("%064x\n", key.PrivateKey.D) + } + return nil +} + +// ExportKeystore to file +func ExportKeystore(address, path, passphrase string) (string, error) { + ks := store.FromAddress(address) + allAccounts := ks.Accounts() + dirPath, err := filepath.Abs(path) + if err != nil { + return "", err + } + outFile := filepath.Join(dirPath, fmt.Sprintf("%s.key", address)) + for _, account := range allAccounts { + keyFile, err := ks.Export(keystore.Account{Address: account.Address}, passphrase, passphrase) + if err != nil { + return "", err + } + e := writeToFile(outFile, string(keyFile)) + if e != nil { + return "", e + } + } + return outFile, nil +} diff --git a/pkg/account/import.go b/pkg/account/import.go new file mode 100644 index 00000000..39168f65 --- /dev/null +++ b/pkg/account/import.go @@ -0,0 +1,132 @@ +package account + +import ( + "encoding/hex" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/mitchellh/go-homedir" + + "github.com/btcsuite/btcd/btcec" + mapset "github.com/deckarep/golang-set" + "github.com/fbsobreira/gotron-sdk/pkg/common" + "github.com/fbsobreira/gotron-sdk/pkg/keystore" + "github.com/fbsobreira/gotron-sdk/pkg/mnemonic" + "github.com/fbsobreira/gotron-sdk/pkg/store" +) + +// ImportFromPrivateKey allows import of an ECDSA private key +func ImportFromPrivateKey(privateKey, name, passphrase string) (string, error) { + privateKey = strings.TrimPrefix(privateKey, "0x") + + if name == "" { + name = generateName() + "-imported" + for store.DoesNamedAccountExist(name) { + name = generateName() + "-imported" + } + } else if store.DoesNamedAccountExist(name) { + return "", fmt.Errorf("account %s already exists", name) + } + + privateKeyBytes, err := hex.DecodeString(privateKey) + if err != nil { + return "", err + } + if len(privateKeyBytes) != common.Secp256k1PrivateKeyBytesLength { + return "", common.ErrBadKeyLength + } + + // btcec.PrivKeyFromBytes only returns a secret key and public key + sk, _ := btcec.PrivKeyFromBytes(btcec.S256(), privateKeyBytes) + ks := store.FromAccountName(name) + _, err = ks.ImportECDSA(sk.ToECDSA(), passphrase) + return name, err +} + +func generateName() string { + words := strings.Split(mnemonic.Generate(), " ") + existingAccounts := mapset.NewSet() + for a := range store.LocalAccounts() { + existingAccounts.Add(a) + } + foundName := false + acct := "" + i := 0 + for { + if foundName { + break + } + if i == len(words)-1 { + words = strings.Split(mnemonic.Generate(), " ") + } + candidate := words[i] + if !existingAccounts.Contains(candidate) { + foundName = true + acct = candidate + break + } + } + return acct +} + +func writeToFile(path string, data string) error { + currDir, _ := os.Getwd() + path, err := filepath.Abs(path) + if err != nil { + return err + } + os.MkdirAll(filepath.Dir(path), 0777) + os.Chdir(filepath.Dir(path)) + file, err := os.Create(filepath.Base(path)) + if err != nil { + return err + } + defer file.Close() + + _, err = io.WriteString(file, data) + if err != nil { + return err + } + os.Chdir(currDir) + return file.Sync() +} + +// ImportKeyStore imports a keystore along with a password +func ImportKeyStore(keyPath, name, passphrase string) (string, error) { + keyPath, err := filepath.Abs(keyPath) + if err != nil { + return "", err + } + keyJSON, readError := ioutil.ReadFile(keyPath) + if readError != nil { + return "", readError + } + if name == "" { + name = generateName() + "-imported" + for store.DoesNamedAccountExist(name) { + name = generateName() + "-imported" + } + } else if store.DoesNamedAccountExist(name) { + return "", fmt.Errorf("account %s already exists", name) + } + key, err := keystore.DecryptKey(keyJSON, passphrase) + if err != nil { + return "", err + } + + hasAddress := store.FromAddress(key.Address.String()) != nil + if hasAddress { + return "", fmt.Errorf("address %s already exists in keystore", key.Address.String()) + } + uDir, _ := homedir.Dir() + newPath := filepath.Join(uDir, common.DefaultConfigDirName, common.DefaultConfigAccountAliasesDirName, name, filepath.Base(keyPath)) + err = writeToFile(newPath, string(keyJSON)) + if err != nil { + return "", err + } + return name, nil +} diff --git a/pkg/account/removal.go b/pkg/account/removal.go new file mode 100644 index 00000000..caed480a --- /dev/null +++ b/pkg/account/removal.go @@ -0,0 +1,27 @@ +package account + +import ( + "fmt" + "os" + "path" + + "github.com/fbsobreira/gotron-sdk/pkg/common" + "github.com/fbsobreira/gotron-sdk/pkg/store" + "github.com/mitchellh/go-homedir" +) + +// RemoveAccount - removes an account from the keystore +func RemoveAccount(name string) error { + accountExists := store.DoesNamedAccountExist(name) + + if !accountExists { + return fmt.Errorf("account %s doesn't exist", name) + } + + uDir, _ := homedir.Dir() + tronCTLDir := path.Join(uDir, common.DefaultConfigDirName, common.DefaultConfigAccountAliasesDirName) + accountDir := fmt.Sprintf("%s/%s", tronCTLDir, name) + os.RemoveAll(accountDir) + + return nil +} diff --git a/pkg/address/address.go b/pkg/address/address.go new file mode 100644 index 00000000..f0ef0844 --- /dev/null +++ b/pkg/address/address.go @@ -0,0 +1,83 @@ +package address + +import ( + "crypto/ecdsa" + "math/big" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/fbsobreira/gotron-sdk/pkg/common" +) + +const ( + // HashLength is the expected length of the hash + HashLength = 32 + // AddressLength is the expected length of the address + AddressLength = 21 + // AddressLengthBase58 is the expected length of the address in base58format + AddressLengthBase58 = 34 + // TronBytePrefix is the hex prefix to address + TronBytePrefix = byte(0x41) + // AmountDecimalPoint TRX decimal point + AmountDecimalPoint = 6 +) + +// Address represents the 21 byte address of an Tron account. +type Address [AddressLength]byte + +// Bytes get bytes from address +func (a Address) Bytes() []byte { + return a[:] +} + +// Hex get bytes from address in string +func (a Address) Hex() string { + return common.ToHex(a[:]) +} + +// SetBytes to address +func (a *Address) SetBytes(b []byte) { + if len(b) > len(a) { + b = b[len(b)-AddressLength:] + } + copy(a[AddressLength-len(b):], b) +} + +// BytesToAddress new address from bytes +func BytesToAddress(b []byte) Address { + var a Address + a.SetBytes(b) + return a +} + +// BigToAddress returns Address with byte values of b. +// If b is larger than len(h), b will be cropped from the left. +func BigToAddress(b *big.Int) Address { return BytesToAddress(b.Bytes()) } + +// HexToAddress returns Address with byte values of s. +// If s is larger than len(h), s will be cropped from the left. +func HexToAddress(s string) Address { return BytesToAddress(common.FromHex(s)) } + +// Base58ToAddress returns Address with byte values of s. +func Base58ToAddress(s string) (Address, error) { + addr, err := common.DecodeCheck(s) + if err != nil { + return Address{}, err + } + return BytesToAddress(addr), nil +} + +// String implements fmt.Stringer. +func (a Address) String() string { + return common.EncodeCheck(a.Bytes()) +} + +// PubkeyToAddress returns address from ecdsa public key +func PubkeyToAddress(p ecdsa.PublicKey) Address { + address := crypto.PubkeyToAddress(p) + + addressTron := make([]byte, AddressLength) + addressTron = append(addressTron, TronBytePrefix) + addressTron = append(addressTron, address.Bytes()...) + + return BytesToAddress(addressTron) +} diff --git a/pkg/client/account.go b/pkg/client/account.go new file mode 100644 index 00000000..f84c6404 --- /dev/null +++ b/pkg/client/account.go @@ -0,0 +1,119 @@ +package client + +import ( + "context" + "fmt" + + "github.com/fbsobreira/gotron-sdk/pkg/common" + "github.com/fbsobreira/gotron-sdk/pkg/proto/api" + "github.com/fbsobreira/gotron-sdk/pkg/proto/core" + "github.com/golang/protobuf/proto" +) + +// GetAccount from BASE58 address +func (g *GrpcClient) GetAccount(address string) (*core.Account, error) { + account := new(core.Account) + var err error + + account.Address, err = common.DecodeCheck(address) + if err != nil { + return nil, err + } + + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + return g.Client.GetAccount(ctx, account) +} + +// GetAccountNet return account resources from BASE58 address +func (g *GrpcClient) GetAccountNet(address string) (*api.AccountNetMessage, error) { + account := new(core.Account) + var err error + + account.Address, err = common.DecodeCheck(address) + if err != nil { + return nil, err + } + + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + return g.Client.GetAccountNet(ctx, account) +} + +// CreateAccount activate tron account +func (g *GrpcClient) CreateAccount(from, accountAddress string) (*api.TransactionExtention, error) { + var err error + + contract := &core.AccountCreateContract{} + if contract.OwnerAddress, err = common.DecodeCheck(from); err != nil { + return nil, err + } + if contract.AccountAddress, err = common.DecodeCheck(accountAddress); err != nil { + return nil, err + } + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + tx, err := g.Client.CreateAccount2(ctx, contract) + if err != nil { + return nil, err + } + if proto.Size(tx) == 0 { + return nil, fmt.Errorf("bad transaction") + } + if tx.GetResult().GetCode() != 0 { + return nil, fmt.Errorf("%s", tx.GetResult().GetMessage()) + } + return tx, nil +} + +// UpdateAccount change account name +func (g *GrpcClient) UpdateAccount(from, accountName string) (*api.TransactionExtention, error) { + var err error + contract := &core.AccountUpdateContract{} + contract.AccountName = []byte(accountName) + if contract.OwnerAddress, err = common.DecodeCheck(from); err != nil { + return nil, err + } + + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + tx, err := g.Client.UpdateAccount2(ctx, contract) + if err != nil { + return nil, err + } + if proto.Size(tx) == 0 { + return nil, fmt.Errorf("bad transaction") + } + if tx.GetResult().GetCode() != 0 { + return nil, fmt.Errorf("%s", tx.GetResult().GetMessage()) + } + return tx, nil +} + +// WithdrawBalance rewards from account +func (g *GrpcClient) WithdrawBalance(from string) (*api.TransactionExtention, error) { + var err error + contract := &core.WithdrawBalanceContract{} + if contract.OwnerAddress, err = common.DecodeCheck(from); err != nil { + return nil, err + } + + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + tx, err := g.Client.WithdrawBalance2(ctx, contract) + if err != nil { + return nil, err + } + if proto.Size(tx) == 0 { + return nil, fmt.Errorf("bad transaction") + } + if tx.GetResult().GetCode() != 0 { + return nil, fmt.Errorf("%s", tx.GetResult().GetMessage()) + } + return tx, nil +} diff --git a/pkg/client/assets.go b/pkg/client/assets.go new file mode 100644 index 00000000..e76458e2 --- /dev/null +++ b/pkg/client/assets.go @@ -0,0 +1,257 @@ +package client + +import ( + "context" + "fmt" + "log" + "strconv" + "time" + + "github.com/fbsobreira/gotron-sdk/pkg/common" + "github.com/fbsobreira/gotron-sdk/pkg/proto/api" + "github.com/fbsobreira/gotron-sdk/pkg/proto/core" + "github.com/golang/protobuf/proto" +) + +// GetAssetIssueByAccount list asset issued by account +func (g *GrpcClient) GetAssetIssueByAccount(address string) (*api.AssetIssueList, error) { + account := new(core.Account) + var err error + + account.Address, err = common.DecodeCheck(address) + if err != nil { + return nil, err + } + + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + return g.Client.GetAssetIssueByAccount(ctx, account) +} + +// GetAssetIssueByName list asset issued by name +func (g *GrpcClient) GetAssetIssueByName(name string) (*core.AssetIssueContract, error) { + + assetName := new(api.BytesMessage) + assetName.Value = []byte(name) + + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + return g.Client.GetAssetIssueByName(ctx, assetName) + +} + +// GetAssetIssueList list all TRC10 +func (g *GrpcClient) GetAssetIssueList() (*api.AssetIssueList, error) { + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + return g.Client.GetAssetIssueList(ctx, new(api.EmptyMessage)) +} + +// AssetIssue create a new asset TRC10 +func (g *GrpcClient) AssetIssue(from, name, description, abbr, urlStr string, + precision int32, totalSupply, startTime, endTime, FreeAssetNetLimit, PublicFreeAssetNetLimit int64, + trxNum, icoNum, voteScore int32, frozenSupply map[string]string) (*api.TransactionExtention, error) { + var err error + + contract := &core.AssetIssueContract{} + if contract.OwnerAddress, err = common.DecodeCheck(from); err != nil { + return nil, err + } + contract.Name = []byte(name) + contract.Abbr = []byte(abbr) + if precision < 0 || precision > 6 { + return nil, fmt.Errorf("create asset issue error: precision < 0 || precision > 6") + } + contract.Precision = precision + if totalSupply <= 0 { + return nil, fmt.Errorf("create asset issue error: total supply <= 0") + } + contract.TotalSupply = totalSupply + if trxNum <= 0 { + return nil, fmt.Errorf("create asset issue error: trxNum <= 0") + } + contract.TrxNum = trxNum + + if icoNum <= 0 { + return nil, fmt.Errorf("create asset issue error: num <= 0") + } + contract.Num = icoNum + + now := time.Now().UnixNano() / 1000000 + if startTime <= now { + return nil, fmt.Errorf("create asset issue error: start time <= current time") + } + contract.StartTime = startTime + + if endTime <= startTime { + return nil, fmt.Errorf("create asset issue error: end time <= start time") + } + contract.EndTime = endTime + + if FreeAssetNetLimit < 0 { + return nil, fmt.Errorf("create asset issue error: free asset net limit < 0") + } + contract.FreeAssetNetLimit = FreeAssetNetLimit + + if PublicFreeAssetNetLimit < 0 { + return nil, fmt.Errorf("create asset issue error: public free asset net limit < 0") + } + contract.PublicFreeAssetNetLimit = PublicFreeAssetNetLimit + + contract.VoteScore = voteScore + contract.Description = []byte(description) + contract.Url = []byte(urlStr) + + for key, value := range frozenSupply { + amount, err := strconv.ParseInt(value, 10, 64) + if err != nil { + log.Fatalf("create asset issue error: convert error: %v", err) + } + days, err := strconv.ParseInt(key, 10, 64) + if err != nil { + log.Fatalf("create asset issue error: convert error: %v", err) + } + assetIssueContractFrozenSupply := new(core. + AssetIssueContract_FrozenSupply) + assetIssueContractFrozenSupply.FrozenAmount = amount + assetIssueContractFrozenSupply.FrozenDays = days + // add supply to contract + contract.FrozenSupply = append(contract. + FrozenSupply, assetIssueContractFrozenSupply) + } + + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + tx, err := g.Client.CreateAssetIssue2(ctx, contract) + if err != nil { + return nil, err + } + if proto.Size(tx) == 0 { + return nil, fmt.Errorf("bad transaction") + } + if tx.GetResult().GetCode() != 0 { + return nil, fmt.Errorf("%s", tx.GetResult().GetMessage()) + } + return tx, nil +} + +// UpdateAssetIssue information +func (g *GrpcClient) UpdateAssetIssue(from, description, urlStr string, + newLimit, newPublicLimit int64) (*api.TransactionExtention, error) { + var err error + + contract := &core.UpdateAssetContract{} + if contract.OwnerAddress, err = common.DecodeCheck(from); err != nil { + return nil, err + } + + contract.Description = []byte(description) + contract.Url = []byte(urlStr) + contract.NewLimit = newLimit + contract.NewPublicLimit = newPublicLimit + + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + tx, err := g.Client.UpdateAsset2(ctx, contract) + if err != nil { + return nil, err + } + if proto.Size(tx) == 0 { + return nil, fmt.Errorf("bad transaction") + } + if tx.GetResult().GetCode() != 0 { + return nil, fmt.Errorf("%s", tx.GetResult().GetMessage()) + } + return tx, nil +} + +// TransferAsset from to base58 address +func (g *GrpcClient) TransferAsset(from, toAddress, + assetName string, amount int64) (*api.TransactionExtention, error) { + var err error + contract := &core.TransferAssetContract{} + if contract.OwnerAddress, err = common.DecodeCheck(from); err != nil { + return nil, err + } + if contract.ToAddress, err = common.DecodeCheck(toAddress); err != nil { + return nil, err + } + + contract.AssetName = []byte(assetName) + contract.Amount = amount + + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + tx, err := g.Client.TransferAsset2(ctx, contract) + if err != nil { + return nil, err + } + if proto.Size(tx) == 0 { + return nil, fmt.Errorf("bad transaction") + } + if tx.GetResult().GetCode() != 0 { + return nil, fmt.Errorf("%s", tx.GetResult().GetMessage()) + } + return tx, nil +} + +// ParticipateAssetIssue TRC10 ICO +func (g *GrpcClient) ParticipateAssetIssue(from, toAddress, + assetName string, amount int64) (*api.TransactionExtention, error) { + var err error + contract := &core.ParticipateAssetIssueContract{} + if contract.OwnerAddress, err = common.DecodeCheck(from); err != nil { + return nil, err + } + if contract.ToAddress, err = common.DecodeCheck(toAddress); err != nil { + return nil, err + } + + contract.AssetName = []byte(assetName) + contract.Amount = amount + + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + tx, err := g.Client.ParticipateAssetIssue2(ctx, contract) + if err != nil { + return nil, err + } + if proto.Size(tx) == 0 { + return nil, fmt.Errorf("bad transaction") + } + if tx.GetResult().GetCode() != 0 { + return nil, fmt.Errorf("%s", tx.GetResult().GetMessage()) + } + return tx, nil +} + +// UnfreezeAsset from owner +func (g *GrpcClient) UnfreezeAsset(from string) (*api.TransactionExtention, error) { + var err error + + contract := &core.UnfreezeAssetContract{} + if contract.OwnerAddress, err = common.DecodeCheck(from); err != nil { + return nil, err + } + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + tx, err := g.Client.UnfreezeAsset2(ctx, contract) + if err != nil { + return nil, err + } + if proto.Size(tx) == 0 { + return nil, fmt.Errorf("bad transaction") + } + if tx.GetResult().GetCode() != 0 { + return nil, fmt.Errorf("%s", tx.GetResult().GetMessage()) + } + return tx, nil +} diff --git a/pkg/client/bank.go b/pkg/client/bank.go new file mode 100644 index 00000000..dd6d1648 --- /dev/null +++ b/pkg/client/bank.go @@ -0,0 +1,81 @@ +package client + +import ( + "context" + "fmt" + + "github.com/fbsobreira/gotron-sdk/pkg/common" + "github.com/fbsobreira/gotron-sdk/pkg/proto/api" + "github.com/fbsobreira/gotron-sdk/pkg/proto/core" + "github.com/golang/protobuf/proto" +) + +// FreezeBalance from base58 address +func (g *GrpcClient) FreezeBalance(from, delegateTo string, resource core.ResourceCode, + frozenBalance, frozenDuration int64) (*api.TransactionExtention, error) { + var err error + + contract := &core.FreezeBalanceContract{} + if contract.OwnerAddress, err = common.DecodeCheck(from); err != nil { + return nil, err + } + + contract.FrozenBalance = frozenBalance + contract.FrozenDuration = frozenDuration + + if len(delegateTo) > 0 { + if contract.ReceiverAddress, err = common.DecodeCheck(delegateTo); err != nil { + return nil, err + } + + } + contract.Resource = resource + + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + tx, err := g.Client.FreezeBalance2(ctx, contract) + if err != nil { + return nil, err + } + if proto.Size(tx) == 0 { + return nil, fmt.Errorf("bad transaction") + } + if tx.GetResult().GetCode() != 0 { + return nil, fmt.Errorf("%s", tx.GetResult().GetMessage()) + } + return tx, nil +} + +// UnfreezeBalance from base58 address +func (g *GrpcClient) UnfreezeBalance(from, delegateTo string, resource core.ResourceCode) (*api.TransactionExtention, error) { + var err error + + contract := &core.UnfreezeBalanceContract{} + if contract.OwnerAddress, err = common.DecodeCheck(from); err != nil { + return nil, err + } + + if len(delegateTo) > 0 { + if contract.ReceiverAddress, err = common.DecodeCheck(delegateTo); err != nil { + return nil, err + } + + } + contract.Resource = resource + + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + tx, err := g.Client.UnfreezeBalance2(ctx, contract) + if err != nil { + return nil, err + } + if proto.Size(tx) == 0 { + return nil, fmt.Errorf("bad transaction") + } + if tx.GetResult().GetCode() != 0 { + return nil, fmt.Errorf("%s", tx.GetResult().GetMessage()) + } + return tx, nil +} diff --git a/pkg/client/block.go b/pkg/client/block.go new file mode 100644 index 00000000..2ceef335 --- /dev/null +++ b/pkg/client/block.go @@ -0,0 +1,85 @@ +package client + +import ( + "context" + "log" + + "github.com/fbsobreira/gotron-sdk/pkg/common" + "github.com/fbsobreira/gotron-sdk/pkg/proto/api" + "github.com/fbsobreira/gotron-sdk/pkg/proto/core" + "go.uber.org/zap" +) + +// GetNowBlock return TIP block +func (g *GrpcClient) GetNowBlock() (*core.Block, error) { + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + result, err := g.Client.GetNowBlock(ctx, new(api.EmptyMessage)) + + if err != nil { + zap.L().Error("Get block now", zap.Error(err)) + return nil, err + } + + return result, nil +} + +// GetBlockByNum block from number +func (g *GrpcClient) GetBlockByNum(num int64) *core.Block { + numMessage := new(api.NumberMessage) + numMessage.Num = num + + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + result, err := g.Client.GetBlockByNum(ctx, numMessage) + + if err != nil { + log.Fatalf("get block by num error: %v", err) + } + + return result +} + +// GetBlockByID block from hash +func (g *GrpcClient) GetBlockByID(id string) (*core.Block, error) { + blockID := new(api.BytesMessage) + var err error + + blockID.Value, err = common.Decode(id) + + if err != nil { + log.Fatalf("get block by id error: %v", err) + } + + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + return g.Client.GetBlockById(ctx, blockID) +} + +// GetBlockByLimitNext return list of block start/end +func (g *GrpcClient) GetBlockByLimitNext(start, end int64) (*api.BlockList, error) { + blockLimit := new(api.BlockLimit) + blockLimit.StartNum = start + blockLimit.EndNum = end + + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + return g.Client.GetBlockByLimitNext(ctx, blockLimit) + +} + +// GetBlockByLatestNum return block list till num +func (g *GrpcClient) GetBlockByLatestNum(num int64) (*api.BlockList, error) { + numMessage := new(api.NumberMessage) + numMessage.Num = num + + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + return g.Client.GetBlockByLatestNum(ctx, numMessage) + +} diff --git a/pkg/client/client.go b/pkg/client/client.go new file mode 100644 index 00000000..3a4dc339 --- /dev/null +++ b/pkg/client/client.go @@ -0,0 +1,37 @@ +package client + +import ( + "time" + + "github.com/fbsobreira/gotron-sdk/pkg/proto/api" + "go.uber.org/zap" + "google.golang.org/grpc" +) + +const grpcTimeout = 5 * time.Second + +// GrpcClient controller structure +type GrpcClient struct { + Address string + Conn *grpc.ClientConn + Client api.WalletClient +} + +// NewGrpcClient create grpc controller +func NewGrpcClient(address string) *GrpcClient { + client := new(GrpcClient) + client.Address = address + return client +} + +// Start initiate grpc connection +func (g *GrpcClient) Start() error { + var err error + g.Conn, err = grpc.Dial(g.Address, grpc.WithInsecure()) + if err != nil { + zap.L().Error("Connecting GRPC Client", zap.Error(err)) + return err + } + g.Client = api.NewWalletClient(g.Conn) + return nil +} diff --git a/pkg/client/network.go b/pkg/client/network.go new file mode 100644 index 00000000..76930ff5 --- /dev/null +++ b/pkg/client/network.go @@ -0,0 +1,90 @@ +package client + +import ( + "context" + "fmt" + + "github.com/fbsobreira/gotron-sdk/pkg/common" + "github.com/fbsobreira/gotron-sdk/pkg/proto/api" + "github.com/fbsobreira/gotron-sdk/pkg/proto/core" + "go.uber.org/zap" +) + +// ListNodes provides list of network nodes +func (g *GrpcClient) ListNodes() (*api.NodeList, error) { + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + nodeList, err := g.Client.ListNodes(ctx, new(api.EmptyMessage)) + if err != nil { + zap.L().Error("List nodes", zap.Error(err)) + } + return nodeList, nil +} + +// GetNextMaintenanceTime get next epoch timestamp +func (g *GrpcClient) GetNextMaintenanceTime() (*api.NumberMessage, error) { + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + return g.Client.GetNextMaintenanceTime(ctx, + new(api.EmptyMessage)) +} + +// TotalTransaction return total transciton in network +func (g *GrpcClient) TotalTransaction() (*api.NumberMessage, error) { + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + return g.Client.TotalTransaction(ctx, + new(api.EmptyMessage)) +} + +//GetTransactionByID returns transaction details by ID +func (g *GrpcClient) GetTransactionByID(id string) (*core.Transaction, error) { + transactionID := new(api.BytesMessage) + var err error + + transactionID.Value, err = common.Decode(id) + if err != nil { + return nil, fmt.Errorf("get transaction by id error: %v", err) + } + + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + return g.Client.GetTransactionById(ctx, transactionID) +} + +//GetTransactionInfoByID returns transaction receipt by ID +func (g *GrpcClient) GetTransactionInfoByID(id string) (*core.TransactionInfo, error) { + transactionID := new(api.BytesMessage) + var err error + + transactionID.Value, err = common.Decode(id) + if err != nil { + return nil, fmt.Errorf("get transaction by id error: %v", err) + } + + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + return g.Client.GetTransactionInfoById(ctx, transactionID) +} + +// Broadcast broadcast TX +func (g *GrpcClient) Broadcast(tx *core.Transaction) (*api.Return, error) { + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + result, err := g.Client.BroadcastTransaction(ctx, tx) + if err != nil { + return nil, err + } + if !result.GetResult() { + return nil, fmt.Errorf("result error: %s", result.GetMessage()) + } + if result.GetCode() != api.Return_SUCCESS { + return nil, fmt.Errorf("result error(%s): %s", result.GetCode, result.GetMessage()) + } + return result, nil +} diff --git a/pkg/client/transaction/controller.go b/pkg/client/transaction/controller.go new file mode 100644 index 00000000..5a4b0e66 --- /dev/null +++ b/pkg/client/transaction/controller.go @@ -0,0 +1,213 @@ +package transaction + +import ( + "crypto/sha256" + "errors" + "fmt" + "time" + + "github.com/fbsobreira/gotron-sdk/pkg/client" + "github.com/fbsobreira/gotron-sdk/pkg/common" + "github.com/fbsobreira/gotron-sdk/pkg/keystore" + "github.com/fbsobreira/gotron-sdk/pkg/ledger" + "github.com/fbsobreira/gotron-sdk/pkg/proto/api" + "github.com/fbsobreira/gotron-sdk/pkg/proto/core" + proto "github.com/golang/protobuf/proto" +) + +var ( + // ErrBadTransactionParam is returned when invalid params are given to the + // controller upon execution of a transaction. + ErrBadTransactionParam = errors.New("transaction has bad parameters") +) + +type sender struct { + ks *keystore.KeyStore + account *keystore.Account +} + +// Controller drives the transaction signing process +type Controller struct { + executionError error + client *client.GrpcClient + tx *core.Transaction + sender sender + Behavior behavior + Receipt *api.Return +} + +type behavior struct { + DryRun bool + SigningImpl SignerImpl + ConfirmationWaitTime uint32 +} + +// NewController initializes a Controller, caller can control behavior via options +func NewController( + client *client.GrpcClient, + senderKs *keystore.KeyStore, + senderAcct *keystore.Account, + tx *core.Transaction, + options ...func(*Controller), +) *Controller { + + ctrlr := &Controller{ + executionError: nil, + client: client, + sender: sender{ + ks: senderKs, + account: senderAcct, + }, + tx: tx, + Behavior: behavior{false, Software, 0}, + } + for _, option := range options { + option(ctrlr) + } + return ctrlr +} + +func (C *Controller) signTxForSending() { + if C.executionError != nil { + return + } + signedTransaction, err := + C.sender.ks.SignTx(*C.sender.account, C.tx) + if err != nil { + C.executionError = err + return + } + C.tx = signedTransaction +} + +func (C *Controller) hardwareSignTxForSending() { + if C.executionError != nil { + return + } + data, _ := C.GetRawData() + signature, err := ledger.SignTx(data) + if err != nil { + C.executionError = err + return + } + + /* TODO: validate signature + if strings.Compare(signerAddr, address.ToBech32(C.sender.account.Address)) != 0 { + C.executionError = ErrBadTransactionParam + errorMsg := "signature verification failed : sender address doesn't match with ledger hardware address" + C.transactionErrors = append(C.transactionErrors, &Error{ + ErrMessage: &errorMsg, + TimestampOfRejection: time.Now().Unix(), + }) + return + } + */ + // add signature + C.tx.Signature = append(C.tx.Signature, signature) +} + +func (C *Controller) TransactionHash() (string, error) { + + rawData, err := C.GetRawData() + if err != nil { + return "", err + } + + h256h := sha256.New() + h256h.Write(rawData) + hash := h256h.Sum(nil) + return common.ToHex(hash), nil +} + +func (C *Controller) txConfirmation() { + if C.executionError != nil || C.Behavior.DryRun { + return + } + if C.Behavior.ConfirmationWaitTime > 0 { + txHash, err := C.TransactionHash() + if err != nil { + C.executionError = fmt.Errorf("could not get tx hash") + return + } + fmt.Printf("His s hash: %s\n", txHash) + start := int(C.Behavior.ConfirmationWaitTime) + for { + // GETTX by ID + _ = txHash + // Add receipt + // if ok return + if start < 0 { + C.executionError = fmt.Errorf("could not confirm transaction after %d seconds", C.Behavior.ConfirmationWaitTime) + return + } + time.Sleep(time.Second) + start-- + } + } + +} + +// ExecuteTransaction is the single entrypoint to execute a plain transaction. +// Each step in transaction creation, execution probably includes a mutation +// Each becomes a no-op if executionError occurred in any previous step +func (C *Controller) ExecuteTransaction( + limitFee uint64, +) error { + switch C.Behavior.SigningImpl { + case Software: + C.signTxForSending() + case Ledger: + C.hardwareSignTxForSending() + } + C.sendSignedTx() + C.txConfirmation() + return C.executionError +} + +// GetRawData Byes from Transaction +func (C *Controller) GetRawData() ([]byte, error) { + return proto.Marshal(C.tx.GetRawData()) +} + +func (C *Controller) sendSignedTx() { + if C.executionError != nil || C.Behavior.DryRun { + return + } + result, err := C.client.Broadcast(C.tx) + if err != nil { + C.executionError = err + return + } + C.Receipt = result +} + +/* +func SignTransaction(transaction *core.Transaction, key *ecdsa.PrivateKey) { + transaction.GetRawData().Timestamp = time.Now().UnixNano() / 1000000 + + rawData, err := proto.Marshal(transaction.GetRawData()) + + if err != nil { + log.Fatalf("sign transaction error: %v", err) + } + + h256h := sha256.New() + h256h.Write(rawData) + hash := h256h.Sum(nil) + + contractList := transaction.GetRawData().GetContract() + + for range contractList { + //TODO: + _ = hash + /*signature, err := crypto.Sign(hash, key) + + if err != nil { + log.Fatalf("sign transaction error: %v", err) + } + + transaction.Signature = append(transaction.Signature, signature) + + } +} +*/ diff --git a/pkg/client/transaction/signer.go b/pkg/client/transaction/signer.go new file mode 100644 index 00000000..ff88a34b --- /dev/null +++ b/pkg/client/transaction/signer.go @@ -0,0 +1,8 @@ +package transaction + +type SignerImpl int + +const ( + Software SignerImpl = iota + Ledger +) diff --git a/pkg/client/transfer.go b/pkg/client/transfer.go new file mode 100644 index 00000000..b2a610ff --- /dev/null +++ b/pkg/client/transfer.go @@ -0,0 +1,40 @@ +package client + +import ( + "context" + "fmt" + + "github.com/fbsobreira/gotron-sdk/pkg/common" + "github.com/fbsobreira/gotron-sdk/pkg/proto/api" + "github.com/fbsobreira/gotron-sdk/pkg/proto/core" + "github.com/golang/protobuf/proto" +) + +// Transfer from to base58 address +func (g *GrpcClient) Transfer(from, toAddress string, amount int64) (*api.TransactionExtention, error) { + var err error + + contract := &core.TransferContract{} + if contract.OwnerAddress, err = common.DecodeCheck(from); err != nil { + return nil, err + } + if contract.ToAddress, err = common.DecodeCheck(toAddress); err != nil { + return nil, err + } + contract.Amount = amount + + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + tx, err := g.Client.CreateTransaction2(ctx, contract) + if err != nil { + return nil, err + } + if proto.Size(tx) == 0 { + return nil, fmt.Errorf("bad transaction") + } + if tx.GetResult().GetCode() != 0 { + return nil, fmt.Errorf("%s", tx.GetResult().GetMessage()) + } + return tx, nil +} diff --git a/pkg/client/witnesses.go b/pkg/client/witnesses.go new file mode 100644 index 00000000..7b12969a --- /dev/null +++ b/pkg/client/witnesses.go @@ -0,0 +1,115 @@ +package client + +import ( + "context" + "fmt" + "strconv" + + "github.com/fbsobreira/gotron-sdk/pkg/common" + "github.com/fbsobreira/gotron-sdk/pkg/proto/api" + "github.com/fbsobreira/gotron-sdk/pkg/proto/core" + "github.com/golang/protobuf/proto" +) + +// ListWitnesses return all witnesses +func (g *GrpcClient) ListWitnesses() (*api.WitnessList, error) { + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + return g.Client.ListWitnesses(ctx, new(api.EmptyMessage)) +} + +// CreateWitness upgrade account to network witness +func (g *GrpcClient) CreateWitness(from, urlStr string) (*api.TransactionExtention, error) { + var err error + + contract := &core.WitnessCreateContract{} + if contract.OwnerAddress, err = common.DecodeCheck(from); err != nil { + return nil, err + } + contract.Url = []byte(urlStr) + + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + tx, err := g.Client.CreateWitness2(ctx, contract) + if err != nil { + return nil, err + } + if proto.Size(tx) == 0 { + return nil, fmt.Errorf("bad transaction") + } + if tx.GetResult().GetCode() != 0 { + return nil, fmt.Errorf("%s", tx.GetResult().GetMessage()) + } + return tx, nil +} + +// UpdateWitness change URL info +func (g *GrpcClient) UpdateWitness(from, urlStr string) (*api.TransactionExtention, error) { + var err error + + contract := &core.WitnessUpdateContract{} + if contract.OwnerAddress, err = common.DecodeCheck(from); err != nil { + return nil, err + } + contract.UpdateUrl = []byte(urlStr) + + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + tx, err := g.Client.UpdateWitness2(ctx, contract) + if err != nil { + return nil, err + } + if proto.Size(tx) == 0 { + return nil, fmt.Errorf("bad transaction") + } + if tx.GetResult().GetCode() != 0 { + return nil, fmt.Errorf("%s", tx.GetResult().GetMessage()) + } + return tx, nil +} + +// VoteWitnessAccount change account vote +func (g *GrpcClient) VoteWitnessAccount(from string, + witnessMap map[string]string) (*api.TransactionExtention, error) { + var err error + + contract := &core.VoteWitnessContract{} + if contract.OwnerAddress, err = common.DecodeCheck(from); err != nil { + return nil, err + } + + for key, value := range witnessMap { + + if witnessAddress, err := common.DecodeCheck(key); err == nil { + if voteCount, err := strconv.ParseInt(value, 64, 10); err == nil { + vote := &core.VoteWitnessContract_Vote{ + VoteAddress: witnessAddress, + VoteCount: voteCount, + } + contract.Votes = append(contract.Votes, vote) + } else { + return nil, err + } + } else { + return nil, err + } + } + + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) + defer cancel() + + tx, err := g.Client.VoteWitnessAccount2(ctx, contract) + if err != nil { + return nil, err + } + if proto.Size(tx) == 0 { + return nil, fmt.Errorf("bad transaction") + } + if tx.GetResult().GetCode() != 0 { + return nil, fmt.Errorf("%s", tx.GetResult().GetMessage()) + } + return tx, nil +} diff --git a/common/base58/base58.go b/pkg/common/base58.go similarity index 73% rename from common/base58/base58.go rename to pkg/common/base58.go index 838508e1..21973063 100644 --- a/common/base58/base58.go +++ b/pkg/common/base58.go @@ -1,15 +1,14 @@ -package base58 +package common import ( "crypto/sha256" + "fmt" + "github.com/shengdoushi/base58" - "log" ) -var tronAlphabet = base58.NewAlphabet("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") - func Encode(input []byte) string { - return base58.Encode(input, tronAlphabet) + return base58.Encode(input, base58.BitcoinAlphabet) } func EncodeCheck(input []byte) string { @@ -28,18 +27,18 @@ func EncodeCheck(input []byte) string { } func Decode(input string) ([]byte, error) { - return base58.Decode(input, tronAlphabet) + return base58.Decode(input, base58.BitcoinAlphabet) } -func DecodeCheck(input string) []byte { +func DecodeCheck(input string) ([]byte, error) { decodeCheck, err := Decode(input) if err != nil { - log.Fatalln(err.Error()) + return nil, err } if len(decodeCheck) < 4 { - return nil + return nil, fmt.Errorf("b58 check error") } decodeData := decodeCheck[:len(decodeCheck)-4] @@ -56,8 +55,7 @@ func DecodeCheck(input string) []byte { h1[1] == decodeCheck[len(decodeData)+1] && h1[2] == decodeCheck[len(decodeData)+2] && h1[3] == decodeCheck[len(decodeData)+3] { - return decodeData + return decodeData, nil } - - return nil + return nil, fmt.Errorf("b58 check error") } diff --git a/common/hexutil/hexutils.go b/pkg/common/hexutils.go similarity index 93% rename from common/hexutil/hexutils.go rename to pkg/common/hexutils.go index 58ef37ab..c3777ac2 100644 --- a/common/hexutil/hexutils.go +++ b/pkg/common/hexutils.go @@ -1,4 +1,4 @@ -package hexutil +package common import "encoding/hex" @@ -14,16 +14,16 @@ func (h *hexError) Error() string { return h.msg } -// Encode encodes bytes as a hex string. -func Encode(bytes []byte) string { +// BytesToHexString encodes bytes as a hex string. +func BytesToHexString(bytes []byte) string { encode := make([]byte, len(bytes)*2) hex.Encode(encode, bytes) return string(encode) } -// Decode hex string as bytes -func Decode(input string) ([]byte, error) { +// HexStringToBytes hex string as bytes +func HexStringToBytes(input string) ([]byte, error) { if len(input) == 0 { return nil, EmptyString } diff --git a/pkg/common/numeric/numeric.go b/pkg/common/numeric/numeric.go new file mode 100644 index 00000000..db590bbd --- /dev/null +++ b/pkg/common/numeric/numeric.go @@ -0,0 +1,666 @@ +package numeric + +// Incorporated from cosmos-sdk + +import ( + "encoding/json" + "errors" + "fmt" + "math/big" + "regexp" + "strconv" + "strings" +) + +// Dec represent a decimal. NOTE: never use new(Dec) or else we will panic unmarshalling into the +// nil embedded big.Int +type Dec struct { + *big.Int `json:"int"` +} + +// number of decimal places +const ( + Precision = 18 + + // bytes required to represent the above precision + // Ceiling[Log2[999 999 999 999 999 999]] + DecimalPrecisionBits = 60 +) + +var ( + precisionReuse = new(big.Int).Exp(big.NewInt(10), big.NewInt(Precision), nil) + fivePrecision = new(big.Int).Quo(precisionReuse, big.NewInt(2)) + precisionMultipliers []*big.Int + zeroInt = big.NewInt(0) + oneInt = big.NewInt(1) + tenInt = big.NewInt(10) +) + +// Set precision multipliers +func init() { + precisionMultipliers = make([]*big.Int, Precision+1) + for i := 0; i <= Precision; i++ { + precisionMultipliers[i] = calcPrecisionMultiplier(int64(i)) + } +} + +func precisionInt() *big.Int { + return new(big.Int).Set(precisionReuse) +} + +// ZeroDec ... +func ZeroDec() Dec { return Dec{new(big.Int).Set(zeroInt)} } + +// OneDec ... +func OneDec() Dec { return Dec{precisionInt()} } + +// SmallestDec ... +func SmallestDec() Dec { return Dec{new(big.Int).Set(oneInt)} } + +// calculate the precision multiplier +func calcPrecisionMultiplier(prec int64) *big.Int { + if prec > Precision { + panic(fmt.Sprintf("too much precision, maximum %v, provided %v", Precision, prec)) + } + zerosToAdd := Precision - prec + multiplier := new(big.Int).Exp(tenInt, big.NewInt(zerosToAdd), nil) + return multiplier +} + +// get the precision multiplier, do not mutate result +func precisionMultiplier(prec int64) *big.Int { + if prec > Precision { + panic(fmt.Sprintf("too much precision, maximum %v, provided %v", Precision, prec)) + } + return precisionMultipliers[prec] +} + +//______________________________________________________________________________________________ + +// NewDec creates a new Dec from integer assuming whole number +func NewDec(i int64) Dec { + return NewDecWithPrec(i, 0) +} + +// NewDecWithPrec creates a new Dec from integer with decimal place at prec +// CONTRACT: prec <= Precision +func NewDecWithPrec(i, prec int64) Dec { + return Dec{ + new(big.Int).Mul(big.NewInt(i), precisionMultiplier(prec)), + } +} + +// NewDecFromBigInt creates a new Dec from big integer assuming whole numbers +// CONTRACT: prec <= Precision +func NewDecFromBigInt(i *big.Int) Dec { + return NewDecFromBigIntWithPrec(i, 0) +} + +// NewDecFromBigIntWithPrec creates a new Dec from big integer assuming whole numbers +// CONTRACT: prec <= Precision +func NewDecFromBigIntWithPrec(i *big.Int, prec int64) Dec { + return Dec{ + new(big.Int).Mul(i, precisionMultiplier(prec)), + } +} + +// NewDecFromInt creates a new Dec from big integer assuming whole numbers +// CONTRACT: prec <= Precision +func NewDecFromInt(i *big.Int) Dec { + return NewDecFromIntWithPrec(i, 0) +} + +// NewDecFromIntWithPrec creates a new Dec from big integer with decimal place at prec +// CONTRACT: prec <= Precision +func NewDecFromIntWithPrec(i *big.Int, prec int64) Dec { + return Dec{ + new(big.Int).Mul(i, precisionMultiplier(prec)), + } +} + +// NewDecFromStr creates a decimal from an input decimal string. +// valid must come in the form: +// (-) whole integers (.) decimal integers +// examples of acceptable input include: +// -123.456 +// 456.7890 +// 345 +// -456789 +// +// NOTE - An error will return if more decimal places +// are provided in the string than the constant Precision. +// +// CONTRACT - This function does not mutate the input str. +func NewDecFromStr(str string) (d Dec, err error) { + if len(str) == 0 { + return d, errors.New("decimal string is empty") + } + + // first extract any negative symbol + neg := false + if str[0] == '-' { + neg = true + str = str[1:] + } + + if len(str) == 0 { + return d, errors.New("decimal string is empty") + } + + strs := strings.Split(str, ".") + lenDecs := 0 + combinedStr := strs[0] + + if len(strs) == 2 { // has a decimal place + lenDecs = len(strs[1]) + if lenDecs == 0 || len(combinedStr) == 0 { + return d, errors.New("bad decimal length") + } + combinedStr += strs[1] + + } else if len(strs) > 2 { + return d, errors.New("too many periods to be a decimal string") + } + + if lenDecs > Precision { + return d, fmt.Errorf("too much precision, maximum %v, len decimal %v", Precision, lenDecs) + } + + // add some extra zero's to correct to the Precision factor + zerosToAdd := Precision - lenDecs + zeros := fmt.Sprintf(`%0`+strconv.Itoa(zerosToAdd)+`s`, "") + combinedStr += zeros + + combined, ok := new(big.Int).SetString(combinedStr, 10) // base 10 + if !ok { + return d, fmt.Errorf("bad string to integer conversion, combinedStr: %v", combinedStr) + } + if neg { + combined = new(big.Int).Neg(combined) + } + return Dec{combined}, nil +} + +// MustNewDecFromStr Decimal from string, panic on error +func MustNewDecFromStr(s string) Dec { + dec, err := NewDecFromStr(s) + if err != nil { + panic(err) + } + return dec +} + +// IsNil ... +func (d Dec) IsNil() bool { return d.Int == nil } // is decimal nil +// IsZero ... +func (d Dec) IsZero() bool { return (d.Int).Sign() == 0 } // is equal to zero +// IsNegative ... +func (d Dec) IsNegative() bool { return (d.Int).Sign() == -1 } // is negative +// IsPositive ... +func (d Dec) IsPositive() bool { return (d.Int).Sign() == 1 } // is positive +// Equal ... +func (d Dec) Equal(d2 Dec) bool { return (d.Int).Cmp(d2.Int) == 0 } // equal decimals +// GT ... +func (d Dec) GT(d2 Dec) bool { return (d.Int).Cmp(d2.Int) > 0 } // greater than +// GTE ... +func (d Dec) GTE(d2 Dec) bool { return (d.Int).Cmp(d2.Int) >= 0 } // greater than or equal +// LT ... +func (d Dec) LT(d2 Dec) bool { return (d.Int).Cmp(d2.Int) < 0 } // less than +// LTE ... +func (d Dec) LTE(d2 Dec) bool { return (d.Int).Cmp(d2.Int) <= 0 } // less than or equal +// Neg ... +func (d Dec) Neg() Dec { return Dec{new(big.Int).Neg(d.Int)} } // reverse the decimal sign +// Abs ... +func (d Dec) Abs() Dec { return Dec{new(big.Int).Abs(d.Int)} } // absolute value + +// Add addition +func (d Dec) Add(d2 Dec) Dec { + res := new(big.Int).Add(d.Int, d2.Int) + + if res.BitLen() > 255+DecimalPrecisionBits { + panic("Int overflow") + } + return Dec{res} +} + +// Sub subtraction +func (d Dec) Sub(d2 Dec) Dec { + res := new(big.Int).Sub(d.Int, d2.Int) + + if res.BitLen() > 255+DecimalPrecisionBits { + panic("Int overflow") + } + return Dec{res} +} + +// Mul multiplication +func (d Dec) Mul(d2 Dec) Dec { + mul := new(big.Int).Mul(d.Int, d2.Int) + chopped := chopPrecisionAndRound(mul) + + if chopped.BitLen() > 255+DecimalPrecisionBits { + panic("Int overflow") + } + return Dec{chopped} +} + +// MulTruncate multiplication truncate +func (d Dec) MulTruncate(d2 Dec) Dec { + mul := new(big.Int).Mul(d.Int, d2.Int) + chopped := chopPrecisionAndTruncate(mul) + + if chopped.BitLen() > 255+DecimalPrecisionBits { + panic("Int overflow") + } + return Dec{chopped} +} + +// MulInt multiplication +func (d Dec) MulInt(i *big.Int) Dec { + mul := new(big.Int).Mul(d.Int, i) + + if mul.BitLen() > 255+DecimalPrecisionBits { + panic("Int overflow") + } + return Dec{mul} +} + +// MulInt64 - multiplication with int64 +func (d Dec) MulInt64(i int64) Dec { + mul := new(big.Int).Mul(d.Int, big.NewInt(i)) + + if mul.BitLen() > 255+DecimalPrecisionBits { + panic("Int overflow") + } + return Dec{mul} +} + +// Quo quotient +func (d Dec) Quo(d2 Dec) Dec { + + // multiply precision twice + mul := new(big.Int).Mul(d.Int, precisionReuse) + mul.Mul(mul, precisionReuse) + + quo := new(big.Int).Quo(mul, d2.Int) + chopped := chopPrecisionAndRound(quo) + + if chopped.BitLen() > 255+DecimalPrecisionBits { + panic("Int overflow") + } + return Dec{chopped} +} + +// QuoTruncate quotient truncate +func (d Dec) QuoTruncate(d2 Dec) Dec { + + // multiply precision twice + mul := new(big.Int).Mul(d.Int, precisionReuse) + mul.Mul(mul, precisionReuse) + + quo := new(big.Int).Quo(mul, d2.Int) + chopped := chopPrecisionAndTruncate(quo) + + if chopped.BitLen() > 255+DecimalPrecisionBits { + panic("Int overflow") + } + return Dec{chopped} +} + +// QuoRoundUp quotient, round up +func (d Dec) QuoRoundUp(d2 Dec) Dec { + // multiply precision twice + mul := new(big.Int).Mul(d.Int, precisionReuse) + mul.Mul(mul, precisionReuse) + + quo := new(big.Int).Quo(mul, d2.Int) + chopped := chopPrecisionAndRoundUp(quo) + + if chopped.BitLen() > 255+DecimalPrecisionBits { + panic("Int overflow") + } + return Dec{chopped} +} + +// QuoInt quotient +func (d Dec) QuoInt(i *big.Int) Dec { + mul := new(big.Int).Quo(d.Int, i) + return Dec{mul} +} + +// QuoInt64 - quotient with int64 +func (d Dec) QuoInt64(i int64) Dec { + mul := new(big.Int).Quo(d.Int, big.NewInt(i)) + return Dec{mul} +} + +// IsInteger is integer, e.g. decimals are zero +func (d Dec) IsInteger() bool { + return new(big.Int).Rem(d.Int, precisionReuse).Sign() == 0 +} + +// Format decimal state +func (d Dec) Format(s fmt.State, verb rune) { + _, err := s.Write([]byte(d.String())) + if err != nil { + panic(err) + } +} + +func (d Dec) String() string { + if d.Int == nil { + return d.Int.String() + } + + isNeg := d.IsNegative() + if d.IsNegative() { + d = d.Neg() + } + + bzInt, err := d.Int.MarshalText() + if err != nil { + return "" + } + inputSize := len(bzInt) + + var bzStr []byte + + // TODO: Remove trailing zeros + // case 1, purely decimal + if inputSize <= Precision { + bzStr = make([]byte, Precision+2) + + // 0. prefix + bzStr[0] = byte('0') + bzStr[1] = byte('.') + + // set relevant digits to 0 + for i := 0; i < Precision-inputSize; i++ { + bzStr[i+2] = byte('0') + } + + // set final digits + copy(bzStr[2+(Precision-inputSize):], bzInt) + + } else { + + // inputSize + 1 to account for the decimal point that is being added + bzStr = make([]byte, inputSize+1) + decPointPlace := inputSize - Precision + + copy(bzStr, bzInt[:decPointPlace]) // pre-decimal digits + bzStr[decPointPlace] = byte('.') // decimal point + copy(bzStr[decPointPlace+1:], bzInt[decPointPlace:]) // post-decimal digits + } + + if isNeg { + return "-" + string(bzStr) + } + + return string(bzStr) +} + +// ____ +// __| |__ "chop 'em +// ` \ round!" +// ___|| ~ _ -bankers +// | | __ +// | | | __|__|__ +// |_____: / | $$$ | +// |________| + +// nolint - go-cyclo +// Remove a Precision amount of rightmost digits and perform bankers rounding +// on the remainder (gaussian rounding) on the digits which have been removed. +// +// Mutates the input. Use the non-mutative version if that is undesired +func chopPrecisionAndRound(d *big.Int) *big.Int { + + // remove the negative and add it back when returning + if d.Sign() == -1 { + // make d positive, compute chopped value, and then un-mutate d + d = d.Neg(d) + d = chopPrecisionAndRound(d) + d = d.Neg(d) + return d + } + + // get the truncated quotient and remainder + quo, rem := d, big.NewInt(0) + quo, rem = quo.QuoRem(d, precisionReuse, rem) + + if rem.Sign() == 0 { // remainder is zero + return quo + } + + switch rem.Cmp(fivePrecision) { + case -1: + return quo + case 1: + return quo.Add(quo, oneInt) + default: // bankers rounding must take place + // always round to an even number + if quo.Bit(0) == 0 { + return quo + } + return quo.Add(quo, oneInt) + } +} + +func chopPrecisionAndRoundUp(d *big.Int) *big.Int { + + // remove the negative and add it back when returning + if d.Sign() == -1 { + // make d positive, compute chopped value, and then un-mutate d + d = d.Neg(d) + // truncate since d is negative... + d = chopPrecisionAndTruncate(d) + d = d.Neg(d) + return d + } + + // get the truncated quotient and remainder + quo, rem := d, big.NewInt(0) + quo, rem = quo.QuoRem(d, precisionReuse, rem) + + if rem.Sign() == 0 { // remainder is zero + return quo + } + + return quo.Add(quo, oneInt) +} + +func chopPrecisionAndRoundNonMutative(d *big.Int) *big.Int { + tmp := new(big.Int).Set(d) + return chopPrecisionAndRound(tmp) +} + +// RoundInt64 rounds the decimal using bankers rounding +func (d Dec) RoundInt64() int64 { + chopped := chopPrecisionAndRoundNonMutative(d.Int) + if !chopped.IsInt64() { + panic("Int64() out of bound") + } + return chopped.Int64() +} + +// RoundInt round the decimal using bankers rounding +func (d Dec) RoundInt() *big.Int { + return chopPrecisionAndRoundNonMutative(d.Int) +} + +//___________________________________________________________________________________ + +// similar to chopPrecisionAndRound, but always rounds down +func chopPrecisionAndTruncate(d *big.Int) *big.Int { + return d.Quo(d, precisionReuse) +} + +func chopPrecisionAndTruncateNonMutative(d *big.Int) *big.Int { + tmp := new(big.Int).Set(d) + return chopPrecisionAndTruncate(tmp) +} + +// TruncateInt64 truncates the decimals from the number and returns an int64 +func (d Dec) TruncateInt64() int64 { + chopped := chopPrecisionAndTruncateNonMutative(d.Int) + if !chopped.IsInt64() { + panic("Int64() out of bound") + } + return chopped.Int64() +} + +// TruncateInt truncates the decimals from the number and returns an Int +func (d Dec) TruncateInt() *big.Int { + return chopPrecisionAndTruncateNonMutative(d.Int) +} + +// TruncateDec truncates the decimals from the number and returns a Dec +func (d Dec) TruncateDec() Dec { + return NewDecFromBigInt(chopPrecisionAndTruncateNonMutative(d.Int)) +} + +// Ceil returns the smallest interger value (as a decimal) that is greater than +// or equal to the given decimal. +func (d Dec) Ceil() Dec { + tmp := new(big.Int).Set(d.Int) + + quo, rem := tmp, big.NewInt(0) + quo, rem = quo.QuoRem(tmp, precisionReuse, rem) + + // no need to round with a zero remainder regardless of sign + if rem.Cmp(zeroInt) == 0 { + return NewDecFromBigInt(quo) + } + + if rem.Sign() == -1 { + return NewDecFromBigInt(quo) + } + + return NewDecFromBigInt(quo.Add(quo, oneInt)) +} + +//___________________________________________________________________________________ + +// MarshalJSON marshals the decimal +func (d Dec) MarshalJSON() ([]byte, error) { + if d.Int == nil { + return []byte{}, nil + } + + return json.Marshal(d.String()) +} + +// UnmarshalJSON defines custom decoding scheme +func (d *Dec) UnmarshalJSON(bz []byte) error { + if d.Int == nil { + d.Int = new(big.Int) + } + + var text string + err := json.Unmarshal(bz, &text) + if err != nil { + return err + } + // TODO: Reuse dec allocation + newDec, err := NewDecFromStr(text) + if err != nil { + return err + } + d.Int = newDec.Int + return nil +} + +// MarshalYAML returns Ythe AML representation. +func (d Dec) MarshalYAML() (interface{}, error) { return d.String(), nil } + +//___________________________________________________________________________________ +// helpers + +// DecsEqual test if two decimal arrays are equal +func DecsEqual(d1s, d2s []Dec) bool { + if len(d1s) != len(d2s) { + return false + } + + for i, d1 := range d1s { + if !d1.Equal(d2s[i]) { + return false + } + } + return true +} + +// MinDec minimum decimal between two +func MinDec(d1, d2 Dec) Dec { + if d1.LT(d2) { + return d1 + } + return d2 +} + +// MaxDec maximum decimal between two +func MaxDec(d1, d2 Dec) Dec { + if d1.LT(d2) { + return d2 + } + return d1 +} + +var ( + pattern, _ = regexp.Compile("[0-9]+\\.{0,1}[0-9]*e-{0,1}[0-9]+") +) + +// Pow calcs power of numeric with int +func Pow(base Dec, exp int) Dec { + if exp < 0 { + return Pow(NewDec(1).Quo(base), -exp) + } + result := NewDec(1) + for { + if exp%2 == 1 { + result = result.Mul(base) + } + exp = exp >> 1 + if exp == 0 { + break + } + base = base.Mul(base) + } + return result +} + +// NewDecFromString from string to DEC +func NewDecFromString(i string) (Dec, error) { + if strings.HasPrefix(i, "-") { + return ZeroDec(), fmt.Errorf("can not be negative: %s", i) + } + if pattern.FindString(i) != "" { + tokens := strings.Split(i, "e") + a, _ := NewDecFromStr(tokens[0]) + b, _ := strconv.Atoi(tokens[1]) + return a.Mul(Pow(NewDec(10), b)), nil + } + if strings.HasPrefix(i, ".") { + i = "0" + i + } + return NewDecFromStr(i) + +} + +// NewDecFromHex Assumes Hex string input +// Split into 2 64 bit integers to guarentee 128 bit precision +func NewDecFromHex(str string) Dec { + str = strings.TrimPrefix(str, "0x") + half := len(str) / 2 + right := str[half:] + r, _ := big.NewInt(0).SetString(right, 16) + if half == 0 { + return NewDecFromBigInt(r) + } + left := str[:half] + l, _ := big.NewInt(0).SetString(left, 16) + return NewDecFromBigInt(l).Mul( + Pow(NewDec(16), len(right)), + ).Add(NewDecFromBigInt(r)) +} diff --git a/pkg/common/presentation.go b/pkg/common/presentation.go new file mode 100644 index 00000000..a6e189ff --- /dev/null +++ b/pkg/common/presentation.go @@ -0,0 +1,27 @@ +package common + +import ( + "bytes" + "encoding/json" +) + +func JSONPrettyFormat(in string) string { + var out bytes.Buffer + err := json.Indent(&out, []byte(in), "", " ") + if err != nil { + return in + } + return out.String() +} + +// returns "{}" on failure case +func ToJSONUnsafe(payload interface{}, pretty bool) string { + j, err := json.Marshal(payload) + if err != nil { + return "{}" + } + if pretty { + return JSONPrettyFormat(string(j)) + } + return string(j) +} diff --git a/pkg/common/values.go b/pkg/common/values.go new file mode 100644 index 00000000..e2d76ea7 --- /dev/null +++ b/pkg/common/values.go @@ -0,0 +1,40 @@ +package common + +import ( + "errors" + "os" +) + +const ( + DefaultConfigDirName = ".tronctl" + DefaultConfigAccountAliasesDirName = "account-keys" + DefaultPassphrase = "" + JSONRPCVersion = "2.0" + Secp256k1PrivateKeyBytesLength = 32 +) + +var ( + DebugRPC = false + DebugTransaction = false + ErrNotAbsPath = errors.New("keypath is not absolute path") + ErrBadKeyLength = errors.New("Invalid private key (wrong length)") + ErrFoundNoPass = errors.New("found no passphrase file") +) + +func init() { + if _, enabled := os.LookupEnv("HMY_RPC_DEBUG"); enabled != false { + DebugRPC = true + } + if _, enabled := os.LookupEnv("HMY_TX_DEBUG"); enabled != false { + DebugTransaction = true + } + if _, enabled := os.LookupEnv("HMY_ALL_DEBUG"); enabled != false { + EnableAllVerbose() + } +} + +// EnableAllVerbose sets debug vars to true +func EnableAllVerbose() { + DebugRPC = true + DebugTransaction = true +} diff --git a/pkg/keys/encoding.go b/pkg/keys/encoding.go new file mode 100644 index 00000000..c078c6c1 --- /dev/null +++ b/pkg/keys/encoding.go @@ -0,0 +1,17 @@ +package keys + +import ( + secp256k1 "github.com/btcsuite/btcd/btcec" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +type Dump struct { + PrivateKey, PublicKeyCompressed, PublicKey string +} + +func EncodeHex(sk *secp256k1.PrivateKey, pk *secp256k1.PublicKey) *Dump { + p0 := sk.Serialize() + p1 := pk.SerializeCompressed() + p2 := pk.SerializeUncompressed() + return &Dump{hexutil.Encode(p0), hexutil.Encode(p1), hexutil.Encode(p2)} +} diff --git a/pkg/keys/keys.go b/pkg/keys/keys.go new file mode 100644 index 00000000..9a56f276 --- /dev/null +++ b/pkg/keys/keys.go @@ -0,0 +1,52 @@ +package keys + +import ( + "fmt" + "os" + "path" + "strings" + + "github.com/fbsobreira/gotron-sdk/pkg/address" + "github.com/fbsobreira/gotron-sdk/pkg/keystore" + + // "github.com/ethereum/go-ethereum/crypto" + + homedir "github.com/mitchellh/go-homedir" +) + +func checkAndMakeKeyDirIfNeeded() string { + userDir, _ := homedir.Dir() + tronCTLDir := path.Join(userDir, ".tronctl", "keystore") + if _, err := os.Stat(tronCTLDir); os.IsNotExist(err) { + // Double check with Leo what is right file persmission + os.Mkdir(tronCTLDir, 0700) + } + + return tronCTLDir +} + +func ListKeys(keystoreDir string) { + tronCTLDir := checkAndMakeKeyDirIfNeeded() + scryptN := keystore.StandardScryptN + scryptP := keystore.StandardScryptP + ks := keystore.NewKeyStore(tronCTLDir, scryptN, scryptP) + // keystore.KeyStore + allAccounts := ks.Accounts() + fmt.Printf("Tron Address:%s File URL:\n", strings.Repeat(" ", address.AddressLengthBase58)) + for _, account := range allAccounts { + fmt.Printf("%s\t\t %s\n", account.Address, account.URL) + } +} + +func AddNewKey(password string) { + tronCTLDir := checkAndMakeKeyDirIfNeeded() + scryptN := keystore.StandardScryptN + scryptP := keystore.StandardScryptP + ks := keystore.NewKeyStore(tronCTLDir, scryptN, scryptP) + account, err := ks.NewAccount(password) + if err != nil { + fmt.Printf("new account error: %v\n", err) + } + fmt.Printf("account: %s\n", account.Address) + fmt.Printf("URL: %s\n", account.URL) +} diff --git a/pkg/keys/mnemonic.go b/pkg/keys/mnemonic.go new file mode 100644 index 00000000..fea4b35f --- /dev/null +++ b/pkg/keys/mnemonic.go @@ -0,0 +1,22 @@ +package keys + +import ( + "fmt" + + secp256k1 "github.com/btcsuite/btcd/btcec" + "github.com/cosmos/cosmos-sdk/crypto/keys/hd" + "github.com/tyler-smith/go-bip39" +) + +// FromMnemonicSeedAndPassphrase derive form mnemonic and passphrase at index +func FromMnemonicSeedAndPassphrase(mnemonic, passphrase string, index int) (*secp256k1.PrivateKey, *secp256k1.PublicKey) { + seed := bip39.NewSeed(mnemonic, passphrase) + master, ch := hd.ComputeMastersFromSeed(seed) + private, _ := hd.DerivePrivateKeyForPath( + master, + ch, + fmt.Sprintf("44'/195'/0'/0/%d", index), + ) + + return secp256k1.PrivKeyFromBytes(secp256k1.S256(), private[:]) +} diff --git a/pkg/keystore/account.go b/pkg/keystore/account.go new file mode 100644 index 00000000..9b8e9bcf --- /dev/null +++ b/pkg/keystore/account.go @@ -0,0 +1,160 @@ +package keystore + +import ( + "fmt" + + "github.com/fbsobreira/gotron-sdk/pkg/address" + "github.com/fbsobreira/gotron-sdk/pkg/proto/core" + "golang.org/x/crypto/sha3" +) + +type DerivationPath []uint32 + +// Account represents an Ethereum account located at a specific location defined +// by the optional URL field. +type Account struct { + Address address.Address `json:"address"` // Ethereum account address derived from the key + URL URL `json:"url"` // Optional resource locator within a backend +} + +// Wallet represents a software or hardware wallet that might contain one or more +// accounts (derived from the same seed). +type Wallet interface { + // URL retrieves the canonical path under which this wallet is reachable. It is + // user by upper layers to define a sorting order over all wallets from multiple + // backends. + URL() URL + + // Status returns a textual status to aid the user in the current state of the + // wallet. It also returns an error indicating any failure the wallet might have + // encountered. + Status() (string, error) + + // Open initializes access to a wallet instance. It is not meant to unlock or + // decrypt account keys, rather simply to establish a connection to hardware + // wallets and/or to access derivation seeds. + // + // The passphrase parameter may or may not be used by the implementation of a + // particular wallet instance. The reason there is no passwordless open method + // is to strive towards a uniform wallet handling, oblivious to the different + // backend providers. + // + // Please note, if you open a wallet, you must close it to release any allocated + // resources (especially important when working with hardware wallets). + Open(passphrase string) error + + // Close releases any resources held by an open wallet instance. + Close() error + + // Accounts retrieves the list of signing accounts the wallet is currently aware + // of. For hierarchical deterministic wallets, the list will not be exhaustive, + // rather only contain the accounts explicitly pinned during account derivation. + Accounts() []Account + + // Contains returns whether an account is part of this particular wallet or not. + Contains(account Account) bool + + // Derive attempts to explicitly derive a hierarchical deterministic account at + // the specified derivation path. If requested, the derived account will be added + // to the wallet's tracked account list. + Derive(path DerivationPath, pin bool) (Account, error) + + // SignData requests the wallet to sign the hash of the given data + // It looks up the account specified either solely via its address contained within, + // or optionally with the aid of any location metadata from the embedded URL field. + // + // If the wallet requires additional authentication to sign the request (e.g. + // a password to decrypt the account, or a PIN code o verify the transaction), + // an AuthNeededError instance will be returned, containing infos for the user + // about which fields or actions are needed. The user may retry by providing + // the needed details via SignDataWithPassphrase, or by other means (e.g. unlock + // the account in a keystore). + SignData(account Account, mimeType string, data []byte) ([]byte, error) + + // SignDataWithPassphrase is identical to SignData, but also takes a password + // NOTE: there's an chance that an erroneous call might mistake the two strings, and + // supply password in the mimetype field, or vice versa. Thus, an implementation + // should never echo the mimetype or return the mimetype in the error-response + SignDataWithPassphrase(account Account, passphrase, mimeType string, data []byte) ([]byte, error) + + // SignText requests the wallet to sign the hash of a given piece of data, prefixed + // by the Ethereum prefix scheme + // It looks up the account specified either solely via its address contained within, + // or optionally with the aid of any location metadata from the embedded URL field. + // + // If the wallet requires additional authentication to sign the request (e.g. + // a password to decrypt the account, or a PIN code o verify the transaction), + // an AuthNeededError instance will be returned, containing infos for the user + // about which fields or actions are needed. The user may retry by providing + // the needed details via SignHashWithPassphrase, or by other means (e.g. unlock + // the account in a keystore). + SignText(account Account, text []byte) ([]byte, error) + + // SignTextWithPassphrase is identical to Signtext, but also takes a password + SignTextWithPassphrase(account Account, passphrase string, hash []byte) ([]byte, error) + + // SignTx requests the wallet to sign the given transaction. + // + // It looks up the account specified either solely via its address contained within, + // or optionally with the aid of any location metadata from the embedded URL field. + // + // If the wallet requires additional authentication to sign the request (e.g. + // a password to decrypt the account, or a PIN code to verify the transaction), + // an AuthNeededError instance will be returned, containing infos for the user + // about which fields or actions are needed. The user may retry by providing + // the needed details via SignTxWithPassphrase, or by other means (e.g. unlock + // the account in a keystore). + SignTx(account Account, tx *core.Transaction) (*core.Transaction, error) + + // SignTxWithPassphrase is identical to SignTx, but also takes a password + SignTxWithPassphrase(account Account, passphrase string, tx *core.Transaction) (*core.Transaction, error) +} + +const ( + // WalletArrived is fired when a new wallet is detected either via USB or via + // a filesystem event in the keystore. + WalletArrived WalletEventType = iota + + // WalletOpened is fired when a wallet is successfully opened with the purpose + // of starting any background processes such as automatic key derivation. + WalletOpened + + // WalletDropped ... + WalletDropped +) + +// departure is detected. +type WalletEvent struct { + Wallet Wallet // Wallet instance arrived or departed + Kind WalletEventType // Event type that happened in the system +} + +// WalletEventType represents the different event types that can be fired by +// the wallet subscription subsystem. +type WalletEventType int + +// TextHash is a helper function that calculates a hash for the given message that can be +// safely used to calculate a signature from. +// +// The hash is calulcated as +// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}). +// +// This gives context to the signed message and prevents signing of transactions. +func TextHash(data []byte) []byte { + hash, _ := TextAndHash(data) + return hash +} + +// TextAndHash is a helper function that calculates a hash for the given message that can be +// safely used to calculate a signature from. +// +// The hash is calulcated as +// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}). +// +// This gives context to the signed message and prevents signing of transactions. +func TextAndHash(data []byte) ([]byte, string) { + msg := fmt.Sprintf("\x19Tron Signed Message:\n%d%s", len(data), string(data)) + hasher := sha3.NewLegacyKeccak256() + hasher.Write([]byte(msg)) + return hasher.Sum(nil), msg +} diff --git a/pkg/keystore/account_cache.go b/pkg/keystore/account_cache.go new file mode 100644 index 00000000..9c4087f0 --- /dev/null +++ b/pkg/keystore/account_cache.go @@ -0,0 +1,303 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package keystore + +import ( + "bufio" + "encoding/json" + "fmt" + "os" + "path/filepath" + "sort" + "strings" + "sync" + "time" + + mapset "github.com/deckarep/golang-set" + "github.com/fbsobreira/gotron-sdk/pkg/address" + "go.uber.org/zap" +) + +// Minimum amount of time between cache reloads. This limit applies if the platform does +// not support change notifications. It also applies if the keystore directory does not +// exist yet, the code will attempt to create a watcher at most this often. +const minReloadInterval = 2 * time.Second + +type accountsByURL []Account + +func (s accountsByURL) Len() int { return len(s) } +func (s accountsByURL) Less(i, j int) bool { return s[i].URL.Cmp(s[j].URL) < 0 } +func (s accountsByURL) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// AmbiguousAddrError is returned when attempting to unlock +// an address for which more than one file exists. +type AmbiguousAddrError struct { + Addr address.Address + Matches []Account +} + +func (err *AmbiguousAddrError) Error() string { + files := "" + for i, a := range err.Matches { + files += a.URL.Path + if i < len(err.Matches)-1 { + files += ", " + } + } + return fmt.Sprintf("multiple keys match address (%s)", files) +} + +// accountCache is a live index of all accounts in the keystore. +type accountCache struct { + keydir string + watcher *watcher + mu sync.Mutex + all accountsByURL + byAddr map[address.Address][]Account + throttle *time.Timer + notify chan struct{} + fileC fileCache +} + +func newAccountCache(keydir string) (*accountCache, chan struct{}) { + ac := &accountCache{ + keydir: keydir, + byAddr: make(map[address.Address][]Account), + notify: make(chan struct{}, 1), + fileC: fileCache{all: mapset.NewThreadUnsafeSet()}, + } + ac.watcher = newWatcher(ac) + return ac, ac.notify +} + +func (ac *accountCache) accounts() []Account { + ac.maybeReload() + ac.mu.Lock() + defer ac.mu.Unlock() + cpy := make([]Account, len(ac.all)) + copy(cpy, ac.all) + return cpy +} + +func (ac *accountCache) hasAddress(addr address.Address) bool { + ac.maybeReload() + ac.mu.Lock() + defer ac.mu.Unlock() + return len(ac.byAddr[addr]) > 0 +} + +func (ac *accountCache) add(newAccount Account) { + ac.mu.Lock() + defer ac.mu.Unlock() + + i := sort.Search(len(ac.all), func(i int) bool { return ac.all[i].URL.Cmp(newAccount.URL) >= 0 }) + if i < len(ac.all) && ac.all[i] == newAccount { + return + } + // newAccount is not in the cache. + ac.all = append(ac.all, Account{}) + copy(ac.all[i+1:], ac.all[i:]) + ac.all[i] = newAccount + ac.byAddr[newAccount.Address] = append(ac.byAddr[newAccount.Address], newAccount) +} + +// note: removed needs to be unique here (i.e. both File and Address must be set). +func (ac *accountCache) delete(removed Account) { + ac.mu.Lock() + defer ac.mu.Unlock() + + ac.all = removeAccount(ac.all, removed) + if ba := removeAccount(ac.byAddr[removed.Address], removed); len(ba) == 0 { + delete(ac.byAddr, removed.Address) + } else { + ac.byAddr[removed.Address] = ba + } +} + +// deleteByFile removes an account referenced by the given path. +func (ac *accountCache) deleteByFile(path string) { + ac.mu.Lock() + defer ac.mu.Unlock() + i := sort.Search(len(ac.all), func(i int) bool { return ac.all[i].URL.Path >= path }) + + if i < len(ac.all) && ac.all[i].URL.Path == path { + removed := ac.all[i] + ac.all = append(ac.all[:i], ac.all[i+1:]...) + if ba := removeAccount(ac.byAddr[removed.Address], removed); len(ba) == 0 { + delete(ac.byAddr, removed.Address) + } else { + ac.byAddr[removed.Address] = ba + } + } +} + +func removeAccount(slice []Account, elem Account) []Account { + for i := range slice { + if slice[i] == elem { + return append(slice[:i], slice[i+1:]...) + } + } + return slice +} + +// find returns the cached account for address if there is a unique match. +// The exact matching rules are explained by the documentation of Account. +// Callers must hold ac.mu. +func (ac *accountCache) find(a Account) (Account, error) { + // Limit search to address candidates if possible. + matches := ac.all + if (a.Address != address.Address{}) { + matches = ac.byAddr[a.Address] + } + if a.URL.Path != "" { + // If only the basename is specified, complete the path. + if !strings.ContainsRune(a.URL.Path, filepath.Separator) { + a.URL.Path = filepath.Join(ac.keydir, a.URL.Path) + } + for i := range matches { + if matches[i].URL == a.URL { + return matches[i], nil + } + } + if (a.Address == address.Address{}) { + return Account{}, ErrNoMatch + } + } + switch len(matches) { + case 1: + return matches[0], nil + case 0: + return Account{}, ErrNoMatch + default: + err := &AmbiguousAddrError{Addr: a.Address, Matches: make([]Account, len(matches))} + copy(err.Matches, matches) + sort.Sort(accountsByURL(err.Matches)) + return Account{}, err + } +} + +func (ac *accountCache) maybeReload() { + ac.mu.Lock() + + if ac.watcher.running { + ac.mu.Unlock() + return // A watcher is running and will keep the cache up-to-date. + } + if ac.throttle == nil { + ac.throttle = time.NewTimer(0) + } else { + select { + case <-ac.throttle.C: + default: + ac.mu.Unlock() + return // The cache was reloaded recently. + } + } + // No watcher running, start it. + ac.watcher.start() + ac.throttle.Reset(minReloadInterval) + ac.mu.Unlock() + ac.scanAccounts() +} + +func (ac *accountCache) close() { + ac.mu.Lock() + ac.watcher.close() + if ac.throttle != nil { + ac.throttle.Stop() + } + if ac.notify != nil { + close(ac.notify) + ac.notify = nil + } + ac.mu.Unlock() +} + +// scanAccounts checks if any changes have occurred on the filesystem, and +// updates the account cache accordingly +func (ac *accountCache) scanAccounts() error { + // Scan the entire folder metadata for file changes + creates, deletes, updates, err := ac.fileC.scan(ac.keydir) + if err != nil { + zap.L().Error("Failed to reload keystore contents", zap.Error(err)) + return err + } + + if creates.Cardinality() == 0 && deletes.Cardinality() == 0 && updates.Cardinality() == 0 { + return nil + } + // Create a helper method to scan the contents of the key files + var ( + buf = new(bufio.Reader) + key struct { + Address string `json:"address"` + } + ) + readAccount := func(path string) *Account { + fd, err := os.Open(path) + if err != nil { + fmt.Printf("NFailed to open keys: %v", err) + zap.L().Error("Failed to open keystore file", zap.String("path", path), zap.Error(err)) + return nil + } + defer fd.Close() + buf.Reset(fd) + // Parse the address. + key.Address = "" + err = json.NewDecoder(buf).Decode(&key) + addr := address.HexToAddress(key.Address) + + switch { + case err != nil: + zap.L().Error("Failed to decode keystore key", zap.String("path", path), zap.Error(err)) + case (addr == address.Address{}): + zap.L().Error("Failed to decode keystore key, missing or zero address", zap.String("path", path), zap.Error(err)) + default: + return &Account{ + Address: addr, + URL: URL{Scheme: KeyStoreScheme, Path: path}, + } + } + return nil + } + // Process all the file diffs + start := time.Now() + + for _, p := range creates.ToSlice() { + if a := readAccount(p.(string)); a != nil { + ac.add(*a) + } + } + for _, p := range deletes.ToSlice() { + ac.deleteByFile(p.(string)) + } + for _, p := range updates.ToSlice() { + path := p.(string) + ac.deleteByFile(path) + if a := readAccount(path); a != nil { + ac.add(*a) + } + } + end := time.Now() + + select { + case ac.notify <- struct{}{}: + default: + } + zap.L().Info("Handled keystore changes", zap.Uint64("time", uint64(end.Sub(start)))) + return nil +} diff --git a/pkg/keystore/crypto.go b/pkg/keystore/crypto.go new file mode 100644 index 00000000..241ae4c9 --- /dev/null +++ b/pkg/keystore/crypto.go @@ -0,0 +1,54 @@ +package keystore + +import ( + "crypto/aes" + "crypto/cipher" +) + +func aesCTRXOR(key, inText, iv []byte) ([]byte, error) { + // AES-128 is selected due to size of encryptKey. + aesBlock, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + stream := cipher.NewCTR(aesBlock, iv) + outText := make([]byte, len(inText)) + stream.XORKeyStream(outText, inText) + return outText, err +} + +func aesCBCDecrypt(key, cipherText, iv []byte) ([]byte, error) { + aesBlock, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + decrypter := cipher.NewCBCDecrypter(aesBlock, iv) + paddedPlaintext := make([]byte, len(cipherText)) + decrypter.CryptBlocks(paddedPlaintext, cipherText) + plaintext := pkcs7Unpad(paddedPlaintext) + if plaintext == nil { + return nil, ErrDecrypt + } + return plaintext, err +} + +// From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes +func pkcs7Unpad(in []byte) []byte { + if len(in) == 0 { + return nil + } + + padding := in[len(in)-1] + if int(padding) > len(in) || padding > aes.BlockSize { + return nil + } else if padding == 0 { + return nil + } + + for i := len(in) - 1; i > len(in)-int(padding)-1; i-- { + if in[i] != padding { + return nil + } + } + return in[:len(in)-int(padding)] +} diff --git a/pkg/keystore/errors.go b/pkg/keystore/errors.go new file mode 100644 index 00000000..7f8d20e4 --- /dev/null +++ b/pkg/keystore/errors.go @@ -0,0 +1,68 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package keystore + +import ( + "errors" + "fmt" +) + +// ErrUnknownAccount is returned for any requested operation for which no backend +// provides the specified account. +var ErrUnknownAccount = errors.New("unknown account") + +// ErrUnknownWallet is returned for any requested operation for which no backend +// provides the specified wallet. +var ErrUnknownWallet = errors.New("unknown wallet") + +// ErrNotSupported is returned when an operation is requested from an account +// backend that it does not support. +var ErrNotSupported = errors.New("not supported") + +// ErrInvalidPassphrase is returned when a decryption operation receives a bad +// passphrase. +var ErrInvalidPassphrase = errors.New("invalid passphrase") + +// ErrWalletAlreadyOpen is returned if a wallet is attempted to be opened the +// second time. +var ErrWalletAlreadyOpen = errors.New("wallet already open") + +// ErrWalletClosed is returned if a wallet is attempted to be opened the +// secodn time. +var ErrWalletClosed = errors.New("wallet closed") + +// AuthNeededError is returned by backends for signing requests where the user +// is required to provide further authentication before signing can succeed. +// +// This usually means either that a password needs to be supplied, or perhaps a +// one time PIN code displayed by some hardware device. +type AuthNeededError struct { + Needed string // Extra authentication the user needs to provide +} + +// NewAuthNeededError creates a new authentication error with the extra details +// about the needed fields set. +func NewAuthNeededError(needed string) error { + return &AuthNeededError{ + Needed: needed, + } +} + +// Error implements the standard error interface. +func (err *AuthNeededError) Error() string { + return fmt.Sprintf("authentication needed: %s", err.Needed) +} diff --git a/pkg/keystore/file_cache.go b/pkg/keystore/file_cache.go new file mode 100644 index 00000000..12db7a4b --- /dev/null +++ b/pkg/keystore/file_cache.go @@ -0,0 +1,102 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package keystore + +import ( + "io/ioutil" + "os" + "path/filepath" + "strings" + "sync" + "time" + + mapset "github.com/deckarep/golang-set" + "go.uber.org/zap" +) + +// fileCache is a cache of files seen during scan of keystore. +type fileCache struct { + all mapset.Set // Set of all files from the keystore folder + lastMod time.Time // Last time instance when a file was modified + mu sync.RWMutex +} + +// scan performs a new scan on the given directory, compares against the already +// cached filenames, and returns file sets: creates, deletes, updates. +func (fc *fileCache) scan(keyDir string) (mapset.Set, mapset.Set, mapset.Set, error) { + t0 := time.Now() + + // List all the failes from the keystore folder + files, err := ioutil.ReadDir(keyDir) + if err != nil { + return nil, nil, nil, err + } + t1 := time.Now() + + fc.mu.Lock() + defer fc.mu.Unlock() + + // Iterate all the files and gather their metadata + all := mapset.NewThreadUnsafeSet() + mods := mapset.NewThreadUnsafeSet() + + var newLastMod time.Time + for _, fi := range files { + path := filepath.Join(keyDir, fi.Name()) + // Skip any non-key files from the folder + if nonKeyFile(fi) { + zap.L().Info("Ignoring file on account scan", zap.String("path", path)) + continue + } + // Gather the set of all and fresly modified files + all.Add(path) + + modified := fi.ModTime() + if modified.After(fc.lastMod) { + mods.Add(path) + } + if modified.After(newLastMod) { + newLastMod = modified + } + } + t2 := time.Now() + + // Update the tracked files and return the three sets + deletes := fc.all.Difference(all) // Deletes = previous - current + creates := all.Difference(fc.all) // Creates = current - previous + updates := mods.Difference(creates) // Updates = modified - creates + + fc.all, fc.lastMod = all, newLastMod + t3 := time.Now() + + // Report on the scanning stats and return + zap.L().Info("FS scan times", zap.Uint64("list", uint64(t1.Sub(t0))), zap.Uint64("set", uint64(t2.Sub(t1))), zap.Uint64("diff", uint64(t3.Sub(t2)))) + return creates, deletes, updates, nil +} + +// nonKeyFile ignores editor backups, hidden files and folders/symlinks. +func nonKeyFile(fi os.FileInfo) bool { + // Skip editor backups and UNIX-style hidden files. + if strings.HasSuffix(fi.Name(), "~") || strings.HasPrefix(fi.Name(), ".") { + return true + } + // Skip misc special files, directories (yes, symlinks too). + if fi.IsDir() || fi.Mode()&os.ModeType != 0 { + return true + } + return false +} diff --git a/pkg/keystore/key.go b/pkg/keystore/key.go new file mode 100644 index 00000000..9aa77036 --- /dev/null +++ b/pkg/keystore/key.go @@ -0,0 +1,235 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package keystore + +import ( + "bytes" + "crypto/ecdsa" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/fbsobreira/gotron-sdk/pkg/address" + "github.com/pborman/uuid" +) + +const ( + version = 3 +) + +// Key struct represents the account Key +type Key struct { + ID uuid.UUID // Version 4 "random" for unique id not derived from key data + // to simplify lookups we also store the address + Address address.Address + // we only store privkey as pubkey/address can be derived from it + // privkey in this struct is always in plaintext + PrivateKey *ecdsa.PrivateKey +} + +type keyStore interface { + // Loads and decrypts the key from disk. + GetKey(addr address.Address, filename string, auth string) (*Key, error) + // Writes and encrypts the key. + StoreKey(filename string, k *Key, auth string) error + // Joins filename with the key directory unless it is already absolute. + JoinPath(filename string) string +} + +type plainKeyJSON struct { + Address string `json:"address"` + PrivateKey string `json:"privatekey"` + ID string `json:"id"` + Version int `json:"version"` +} + +type encryptedKeyJSONV3 struct { + Address string `json:"address"` + Crypto CryptoJSON `json:"crypto"` + ID string `json:"id"` + Version int `json:"version"` +} + +type encryptedKeyJSONV1 struct { + Address string `json:"address"` + Crypto CryptoJSON `json:"crypto"` + ID string `json:"id"` + Version string `json:"version"` +} + +// CryptoJSON ... +type CryptoJSON struct { + Cipher string `json:"cipher"` + CipherText string `json:"ciphertext"` + CipherParams cipherparamsJSON `json:"cipherparams"` + KDF string `json:"kdf"` + KDFParams map[string]interface{} `json:"kdfparams"` + MAC string `json:"mac"` +} + +type cipherparamsJSON struct { + IV string `json:"iv"` +} + +// MarshalJSON will marshal the Key object +func (k *Key) MarshalJSON() (j []byte, err error) { + jStruct := plainKeyJSON{ + hex.EncodeToString(k.Address[:]), + hex.EncodeToString(crypto.FromECDSA(k.PrivateKey)), + k.ID.String(), + version, + } + j, err = json.Marshal(jStruct) + return j, err +} + +// UnmarshalJSON will unmarshal a byte array to the Key object +func (k *Key) UnmarshalJSON(j []byte) (err error) { + keyJSON := new(plainKeyJSON) + err = json.Unmarshal(j, &keyJSON) + if err != nil { + return err + } + + u := new(uuid.UUID) + *u = uuid.Parse(keyJSON.ID) + k.ID = *u + addr, err := hex.DecodeString(keyJSON.Address) + if err != nil { + return err + } + privkey, err := crypto.HexToECDSA(keyJSON.PrivateKey) + if err != nil { + return err + } + + k.Address = address.BytesToAddress(addr) + k.PrivateKey = privkey + + return nil +} + +func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key { + id := uuid.NewRandom() + key := &Key{ + ID: id, + Address: address.PubkeyToAddress(privateKeyECDSA.PublicKey), + PrivateKey: privateKeyECDSA, + } + return key +} + +// NewKeyForDirectICAP generates a key whose address fits into < 155 bits so it can fit +// into the Direct ICAP spec. for simplicity and easier compatibility with other libs, we +// retry until the first byte is 0. +func NewKeyForDirectICAP(rand io.Reader) *Key { + randBytes := make([]byte, 64) + _, err := rand.Read(randBytes) + if err != nil { + panic("key generation: could not read from random source: " + err.Error()) + } + reader := bytes.NewReader(randBytes) + privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), reader) + if err != nil { + panic("key generation: ecdsa.GenerateKey failed: " + err.Error()) + } + key := newKeyFromECDSA(privateKeyECDSA) + if !strings.HasPrefix(key.Address.Hex(), "0x00") { + return NewKeyForDirectICAP(rand) + } + return key +} + +func newKey(rand io.Reader) (*Key, error) { + privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand) + if err != nil { + return nil, err + } + return newKeyFromECDSA(privateKeyECDSA), nil +} + +func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, Account, error) { + key, err := newKey(rand) + if err != nil { + return nil, Account{}, err + } + a := Account{ + Address: key.Address, + URL: URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))}, + } + if err := ks.StoreKey(a.URL.Path, key, auth); err != nil { + zeroKey(key.PrivateKey) + return nil, a, err + } + return key, a, err +} + +func writeTemporaryKeyFile(file string, content []byte) (string, error) { + // Create the keystore directory with appropriate permissions + // in case it is not present yet. + const dirPerm = 0700 + if err := os.MkdirAll(filepath.Dir(file), dirPerm); err != nil { + return "", err + } + // Atomic write: create a temporary hidden file first + // then move it into place. TempFile assigns mode 0600. + f, err := ioutil.TempFile(filepath.Dir(file), "."+filepath.Base(file)+".tmp") + if err != nil { + return "", err + } + if _, err := f.Write(content); err != nil { + f.Close() + os.Remove(f.Name()) + return "", err + } + f.Close() + return f.Name(), nil +} + +func writeKeyFile(file string, content []byte) error { + name, err := writeTemporaryKeyFile(file, content) + if err != nil { + return err + } + return os.Rename(name, file) +} + +// keyFileName implements the naming convention for keyfiles: +// UTC---
+func keyFileName(keyAddr address.Address) string { + ts := time.Now().UTC() + return fmt.Sprintf("UTC--%s--%s", toISO8601(ts), hex.EncodeToString(keyAddr[:])) +} + +func toISO8601(t time.Time) string { + var tz string + name, offset := t.Zone() + if name == "UTC" { + tz = "Z" + } else { + tz = fmt.Sprintf("%03d00", offset/3600) + } + return fmt.Sprintf("%04d-%02d-%02dT%02d-%02d-%02d.%09d%s", + t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), tz) +} diff --git a/pkg/keystore/keystore.go b/pkg/keystore/keystore.go new file mode 100644 index 00000000..051bdec3 --- /dev/null +++ b/pkg/keystore/keystore.go @@ -0,0 +1,483 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package keystore implements encrypted storage of secp256k1 private keys. +// +// Keys are stored as encrypted JSON files according to the Web3 Secret Storage specification. +// See https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition for more information. +package keystore + +import ( + "crypto/ecdsa" + crand "crypto/rand" + "errors" + "fmt" + "os" + "path/filepath" + "reflect" + "runtime" + "sync" + "time" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/event" + "github.com/fbsobreira/gotron-sdk/pkg/address" + "github.com/fbsobreira/gotron-sdk/pkg/proto/core" +) + +// ErrLocked ... +var ( + ErrLocked = NewAuthNeededError("password or unlock") + ErrNoMatch = errors.New("no key for given address or file") + ErrDecrypt = errors.New("could not decrypt key with given passphrase") +) + +// KeyStoreType is the reflect type of a keystore backend. +var KeyStoreType = reflect.TypeOf(&KeyStore{}) + +// KeyStoreScheme is the protocol scheme prefixing account and wallet URLs. +const KeyStoreScheme = "keystore" + +// Maximum time between wallet refreshes (if filesystem notifications don't work). +const walletRefreshCycle = 3 * time.Second + +// KeyStore manages a key storage directory on disk. +type KeyStore struct { + storage keyStore // Storage backend, might be cleartext or encrypted + cache *accountCache // In-memory account cache over the filesystem storage + changes chan struct{} // Channel receiving change notifications from the cache + unlocked map[address.Address]*unlocked // Currently unlocked account (decrypted private keys) + + wallets []Wallet // Wallet wrappers around the individual key files + updateFeed event.Feed // Event feed to notify wallet additions/removals + updateScope event.SubscriptionScope // Subscription scope tracking current live listeners + updating bool // Whether the event notification loop is running + + mu sync.RWMutex +} + +type unlocked struct { + *Key + abort chan struct{} +} + +// NewKeyStore creates a keystore for the given directory. +func NewKeyStore(keydir string, scryptN, scryptP int) *KeyStore { + keydir, _ = filepath.Abs(keydir) + ks := &KeyStore{storage: &keyStorePassphrase{keydir, scryptN, scryptP, false}} + ks.init(keydir) + return ks +} + +// NewPlaintextKeyStore creates a keystore for the given directory. +// Deprecated: Use NewKeyStore. +func NewPlaintextKeyStore(keydir string) *KeyStore { + keydir, _ = filepath.Abs(keydir) + ks := &KeyStore{storage: &keyStorePlain{keydir}} + ks.init(keydir) + return ks +} + +func (ks *KeyStore) init(keydir string) { + // Lock the mutex since the account cache might call back with events + ks.mu.Lock() + defer ks.mu.Unlock() + + // Initialize the set of unlocked keys and the account cache + ks.unlocked = make(map[address.Address]*unlocked) + ks.cache, ks.changes = newAccountCache(keydir) + + // TODO: In order for this finalizer to work, there must be no references + // to ks. addressCache doesn't keep a reference but unlocked keys do, + // so the finalizer will not trigger until all timed unlocks have expired. + runtime.SetFinalizer(ks, func(m *KeyStore) { + m.cache.close() + }) + // Create the initial list of wallets from the cache + accs := ks.cache.accounts() + + ks.wallets = make([]Wallet, len(accs)) + for i := 0; i < len(accs); i++ { + ks.wallets[i] = &keystoreWallet{account: accs[i], keystore: ks} + } +} + +// Wallets implements account.Backend, returning all single-key wallets from the +// keystore directory. +func (ks *KeyStore) Wallets() []Wallet { + // Make sure the list of wallets is in sync with the account cache + ks.refreshWallets() + + ks.mu.RLock() + defer ks.mu.RUnlock() + + cpy := make([]Wallet, len(ks.wallets)) + copy(cpy, ks.wallets) + return cpy +} + +// refreshWallets retrieves the current account list and based on that does any +// necessary wallet refreshes. +func (ks *KeyStore) refreshWallets() { + // Retrieve the current list of accounts + ks.mu.Lock() + accs := ks.cache.accounts() + + // Transform the current list of wallets into the new one + var ( + wallets = make([]Wallet, 0, len(accs)) + events []WalletEvent + ) + + for _, account := range accs { + // Drop wallets while they were in front of the next account + for len(ks.wallets) > 0 && ks.wallets[0].URL().Cmp(account.URL) < 0 { + events = append(events, WalletEvent{Wallet: ks.wallets[0], Kind: WalletDropped}) + ks.wallets = ks.wallets[1:] + } + // If there are no more wallets or the account is before the next, wrap new wallet + if len(ks.wallets) == 0 || ks.wallets[0].URL().Cmp(account.URL) > 0 { + wallet := &keystoreWallet{account: account, keystore: ks} + + events = append(events, WalletEvent{Wallet: wallet, Kind: WalletArrived}) + wallets = append(wallets, wallet) + continue + } + // If the account is the same as the first wallet, keep it + if ks.wallets[0].Accounts()[0] == account { + wallets = append(wallets, ks.wallets[0]) + ks.wallets = ks.wallets[1:] + continue + } + } + // Drop any leftover wallets and set the new batch + for _, wallet := range ks.wallets { + events = append(events, WalletEvent{Wallet: wallet, Kind: WalletDropped}) + } + ks.wallets = wallets + ks.mu.Unlock() + + // Fire all wallet events and return + for _, event := range events { + ks.updateFeed.Send(event) + } +} + +// Subscribe implements account.Backend, creating an async subscription to +// receive notifications on the addition or removal of keystore wallets. +func (ks *KeyStore) Subscribe(sink chan<- WalletEvent) event.Subscription { + // We need the mutex to reliably start/stop the update loop + ks.mu.Lock() + defer ks.mu.Unlock() + + // Subscribe the caller and track the subscriber count + sub := ks.updateScope.Track(ks.updateFeed.Subscribe(sink)) + + // Subscribers require an active notification loop, start it + if !ks.updating { + ks.updating = true + go ks.updater() + } + return sub +} + +// updater is responsible for maintaining an up-to-date list of wallets stored in +// the keystore, and for firing wallet addition/removal events. It listens for +// account change events from the underlying account cache, and also periodically +// forces a manual refresh (only triggers for systems where the filesystem notifier +// is not running). +func (ks *KeyStore) updater() { + for { + // Wait for an account update or a refresh timeout + select { + case <-ks.changes: + case <-time.After(walletRefreshCycle): + } + // Run the wallet refresher + ks.refreshWallets() + + // If all our subscribers left, stop the updater + ks.mu.Lock() + if ks.updateScope.Count() == 0 { + ks.updating = false + ks.mu.Unlock() + return + } + ks.mu.Unlock() + } +} + +// HasAddress reports whether a key with the given address is present. +func (ks *KeyStore) HasAddress(addr address.Address) bool { + return ks.cache.hasAddress(addr) +} + +// Accounts returns all key files present in the directory. +func (ks *KeyStore) Accounts() []Account { + return ks.cache.accounts() +} + +// Delete deletes the key matched by account if the passphrase is correct. +// If the account contains no filename, the address must match a unique key. +func (ks *KeyStore) Delete(a Account, passphrase string) error { + // Decrypting the key isn't really necessary, but we do + // it anyway to check the password and zero out the key + // immediately afterwards. + a, key, err := ks.GetDecryptedKey(a, passphrase) + if key != nil { + zeroKey(key.PrivateKey) + } + if err != nil { + return err + } + // The order is crucial here. The key is dropped from the + // cache after the file is gone so that a reload happening in + // between won't insert it into the cache again. + err = os.Remove(a.URL.Path) + if err == nil { + ks.cache.delete(a) + ks.refreshWallets() + } + return err +} + +// SignHash calculates a ECDSA signature for the given hash. The produced +// signature is in the [R || S || V] format where V is 0 or 1. +func (ks *KeyStore) SignHash(a Account, hash []byte) ([]byte, error) { + // Look up the key to sign with and abort if it cannot be found + ks.mu.RLock() + defer ks.mu.RUnlock() + + unlockedKey, found := ks.unlocked[a.Address] + if !found { + return nil, ErrLocked + } + // Sign the hash using plain ECDSA operations + return crypto.Sign(hash, unlockedKey.PrivateKey) +} + +// SignTx signs the given transaction with the requested account. +func (ks *KeyStore) SignTx(a Account, tx *core.Transaction) (*core.Transaction, error) { + // Look up the key to sign with and abort if it cannot be found + ks.mu.RLock() + defer ks.mu.RUnlock() + + unlockedKey, found := ks.unlocked[a.Address] + if !found { + return nil, ErrLocked + } + + //return types.SignTx(tx, types.HomesteadSigner{}, unlockedKey.PrivateKey) + // TODO: + _ = unlockedKey + return nil, nil +} + +// SignHashWithPassphrase signs hash if the private key matching the given address +// can be decrypted with the given passphrase. The produced signature is in the +// [R || S || V] format where V is 0 or 1. +func (ks *KeyStore) SignHashWithPassphrase(a Account, passphrase string, hash []byte) (signature []byte, err error) { + _, key, err := ks.GetDecryptedKey(a, passphrase) + if err != nil { + return nil, err + } + defer zeroKey(key.PrivateKey) + return crypto.Sign(hash, key.PrivateKey) +} + +// SignTxWithPassphrase signs the transaction if the private key matching the +// given address can be decrypted with the given passphrase. +func (ks *KeyStore) SignTxWithPassphrase(a Account, passphrase string, tx *core.Transaction) (*core.Transaction, error) { + _, key, err := ks.GetDecryptedKey(a, passphrase) + if err != nil { + return nil, err + } + defer zeroKey(key.PrivateKey) + + // Depending on the presence of the chain ID, sign with EIP155 or homestead + // TODO: + //return types.SignTx(tx, types.HomesteadSigner{}, key.PrivateKey) + return nil, nil +} + +// Unlock unlocks the given account indefinitely. +func (ks *KeyStore) Unlock(a Account, passphrase string) error { + return ks.TimedUnlock(a, passphrase, 0) +} + +// Lock removes the private key with the given address from memory. +func (ks *KeyStore) Lock(addr address.Address) error { + ks.mu.Lock() + if unl, found := ks.unlocked[addr]; found { + ks.mu.Unlock() + ks.expire(addr, unl, time.Duration(0)*time.Nanosecond) + } else { + ks.mu.Unlock() + } + return nil +} + +// TimedUnlock unlocks the given account with the passphrase. The account +// stays unlocked for the duration of timeout. A timeout of 0 unlocks the account +// until the program exits. The account must match a unique key file. +// +// If the account address is already unlocked for a duration, TimedUnlock extends or +// shortens the active unlock timeout. If the address was previously unlocked +// indefinitely the timeout is not altered. +func (ks *KeyStore) TimedUnlock(a Account, passphrase string, timeout time.Duration) error { + a, key, err := ks.GetDecryptedKey(a, passphrase) + if err != nil { + return err + } + + ks.mu.Lock() + defer ks.mu.Unlock() + u, found := ks.unlocked[a.Address] + if found { + if u.abort == nil { + // The address was unlocked indefinitely, so unlocking + // it with a timeout would be confusing. + zeroKey(key.PrivateKey) + return nil + } + // Terminate the expire goroutine and replace it below. + close(u.abort) + } + if timeout > 0 { + u = &unlocked{Key: key, abort: make(chan struct{})} + go ks.expire(a.Address, u, timeout) + } else { + u = &unlocked{Key: key} + } + ks.unlocked[a.Address] = u + return nil +} + +// Find resolves the given account into a unique entry in the keystore. +func (ks *KeyStore) Find(a Account) (Account, error) { + ks.cache.maybeReload() + ks.cache.mu.Lock() + a, err := ks.cache.find(a) + ks.cache.mu.Unlock() + return a, err +} + +// GetDecryptedKey decrypt and return the key for the account. +func (ks *KeyStore) GetDecryptedKey(a Account, auth string) (Account, *Key, error) { + a, err := ks.Find(a) + if err != nil { + return a, nil, err + } + key, err := ks.storage.GetKey(a.Address, a.URL.Path, auth) + return a, key, err +} + +func (ks *KeyStore) expire(addr address.Address, u *unlocked, timeout time.Duration) { + t := time.NewTimer(timeout) + defer t.Stop() + select { + case <-u.abort: + // just quit + case <-t.C: + ks.mu.Lock() + // only drop if it's still the same key instance that dropLater + // was launched with. we can check that using pointer equality + // because the map stores a new pointer every time the key is + // unlocked. + if ks.unlocked[addr] == u { + zeroKey(u.PrivateKey) + delete(ks.unlocked, addr) + } + ks.mu.Unlock() + } +} + +// NewAccount generates a new key and stores it into the key directory, +// encrypting it with the passphrase. +func (ks *KeyStore) NewAccount(passphrase string) (Account, error) { + _, account, err := storeNewKey(ks.storage, crand.Reader, passphrase) + if err != nil { + return Account{}, err + } + // Add the account to the cache immediately rather + // than waiting for file system notifications to pick it up. + ks.cache.add(account) + ks.refreshWallets() + return account, nil +} + +// Export exports as a JSON key, encrypted with newPassphrase. +func (ks *KeyStore) Export(a Account, passphrase, newPassphrase string) (keyJSON []byte, err error) { + _, key, err := ks.GetDecryptedKey(a, passphrase) + if err != nil { + return nil, err + } + var N, P int + if store, ok := ks.storage.(*keyStorePassphrase); ok { + N, P = store.scryptN, store.scryptP + } else { + N, P = StandardScryptN, StandardScryptP + } + return EncryptKey(key, newPassphrase, N, P) +} + +// Import stores the given encrypted JSON key into the key directory. +func (ks *KeyStore) Import(keyJSON []byte, passphrase, newPassphrase string) (Account, error) { + key, err := DecryptKey(keyJSON, passphrase) + if key != nil && key.PrivateKey != nil { + defer zeroKey(key.PrivateKey) + } + if err != nil { + return Account{}, err + } + return ks.importKey(key, newPassphrase) +} + +// ImportECDSA stores the given key into the key directory, encrypting it with the passphrase. +func (ks *KeyStore) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (Account, error) { + key := newKeyFromECDSA(priv) + if ks.cache.hasAddress(key.Address) { + return Account{}, fmt.Errorf("account already exists") + } + return ks.importKey(key, passphrase) +} + +func (ks *KeyStore) importKey(key *Key, passphrase string) (Account, error) { + a := Account{Address: key.Address, URL: URL{Scheme: KeyStoreScheme, Path: ks.storage.JoinPath(keyFileName(key.Address))}} + if err := ks.storage.StoreKey(a.URL.Path, key, passphrase); err != nil { + return Account{}, err + } + ks.cache.add(a) + ks.refreshWallets() + return a, nil +} + +// Update changes the passphrase of an existing account. +func (ks *KeyStore) Update(a Account, passphrase, newPassphrase string) error { + a, key, err := ks.GetDecryptedKey(a, passphrase) + if err != nil { + return err + } + return ks.storage.StoreKey(a.URL.Path, key, newPassphrase) +} + +// zeroKey zeroes a private key in memory. +func zeroKey(k *ecdsa.PrivateKey) { + b := k.D.Bits() + for i := range b { + b[i] = 0 + } +} diff --git a/pkg/keystore/passphrase.go b/pkg/keystore/passphrase.go new file mode 100644 index 00000000..697fa0f7 --- /dev/null +++ b/pkg/keystore/passphrase.go @@ -0,0 +1,356 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +/* + +This key store behaves as KeyStorePlain with the difference that +the private key is encrypted and on disk uses another JSON encoding. + +The crypto is documented at https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition + +*/ + +package keystore + +import ( + "bytes" + "crypto/aes" + "crypto/rand" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/crypto" + "github.com/fbsobreira/gotron-sdk/pkg/address" + "github.com/pborman/uuid" + "golang.org/x/crypto/pbkdf2" + "golang.org/x/crypto/scrypt" +) + +const ( + keyHeaderKDF = "scrypt" + + // StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB + // memory and taking approximately 1s CPU time on a modern processor. + StandardScryptN = 1 << 18 + + // StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB + // memory and taking approximately 1s CPU time on a modern processor. + StandardScryptP = 1 + + // LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB + // memory and taking approximately 100ms CPU time on a modern processor. + LightScryptN = 1 << 12 + + // LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB + // memory and taking approximately 100ms CPU time on a modern processor. + LightScryptP = 6 + + scryptR = 8 + scryptDKLen = 32 +) + +type keyStorePassphrase struct { + keysDirPath string + scryptN int + scryptP int + // skipKeyFileVerification disables the security-feature which does + // reads and decrypts any newly created keyfiles. This should be 'false' in all + // cases except tests -- setting this to 'true' is not recommended. + skipKeyFileVerification bool +} + +func (ks keyStorePassphrase) GetKey(addr address.Address, filename, auth string) (*Key, error) { + // Load the key from the keystore and decrypt its contents + keyjson, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + key, err := DecryptKey(keyjson, auth) + if err != nil { + return nil, err + } + // Make sure we're really operating on the requested key (no swap attacks) + if key.Address != addr { + return nil, fmt.Errorf("key content mismatch: have account %x, want %x", key.Address, addr) + } + return key, nil +} + +// StoreKey generates a key, encrypts with 'auth' and stores in the given directory +func StoreKey(dir, auth string, scryptN, scryptP int) (Account, error) { + _, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP, false}, rand.Reader, auth) + return a, err +} + +func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) error { + keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP) + if err != nil { + return err + } + // Write into temporary file + tmpName, err := writeTemporaryKeyFile(filename, keyjson) + if err != nil { + return err + } + if !ks.skipKeyFileVerification { + // Verify that we can decrypt the file with the given password. + _, err = ks.GetKey(key.Address, tmpName, auth) + if err != nil { + msg := "An error was encountered when saving and verifying the keystore file. \n" + + "This indicates that the keystore is corrupted. \n" + + "The corrupted file is stored at \n%v\n" + + "Please file a ticket at:\n\n" + + "https://github.com/ethereum/go-ethereum/issues." + + "The error was : %s" + return fmt.Errorf(msg, tmpName, err) + } + } + return os.Rename(tmpName, filename) +} + +func (ks keyStorePassphrase) JoinPath(filename string) string { + if filepath.IsAbs(filename) { + return filename + } + return filepath.Join(ks.keysDirPath, filename) +} + +// EncryptDataV3 encrypts the data given as 'data' with the password 'auth'. +func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error) { + + salt := make([]byte, 32) + if _, err := io.ReadFull(rand.Reader, salt); err != nil { + panic("reading from crypto/rand failed: " + err.Error()) + } + derivedKey, err := scrypt.Key(auth, salt, scryptN, scryptR, scryptP, scryptDKLen) + if err != nil { + return CryptoJSON{}, err + } + encryptKey := derivedKey[:16] + + iv := make([]byte, aes.BlockSize) // 16 + if _, err := io.ReadFull(rand.Reader, iv); err != nil { + panic("reading from crypto/rand failed: " + err.Error()) + } + cipherText, err := aesCTRXOR(encryptKey, data, iv) + if err != nil { + return CryptoJSON{}, err + } + mac := crypto.Keccak256(derivedKey[16:32], cipherText) + + scryptParamsJSON := make(map[string]interface{}, 5) + scryptParamsJSON["n"] = scryptN + scryptParamsJSON["r"] = scryptR + scryptParamsJSON["p"] = scryptP + scryptParamsJSON["dklen"] = scryptDKLen + scryptParamsJSON["salt"] = hex.EncodeToString(salt) + cipherParamsJSON := cipherparamsJSON{ + IV: hex.EncodeToString(iv), + } + + cryptoStruct := CryptoJSON{ + Cipher: "aes-128-ctr", + CipherText: hex.EncodeToString(cipherText), + CipherParams: cipherParamsJSON, + KDF: keyHeaderKDF, + KDFParams: scryptParamsJSON, + MAC: hex.EncodeToString(mac), + } + return cryptoStruct, nil +} + +// EncryptKey encrypts a key using the specified scrypt parameters into a json +// blob that can be decrypted later on. +func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) { + keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32) + cryptoStruct, err := EncryptDataV3(keyBytes, []byte(auth), scryptN, scryptP) + if err != nil { + return nil, err + } + encryptedKeyJSONV3 := encryptedKeyJSONV3{ + hex.EncodeToString(key.Address[:]), + cryptoStruct, + key.ID.String(), + version, + } + return json.Marshal(encryptedKeyJSONV3) +} + +// DecryptKey decrypts a key from a json blob, returning the private key itself. +func DecryptKey(keyjson []byte, auth string) (*Key, error) { + // Parse the json into a simple map to fetch the key version + m := make(map[string]interface{}) + if err := json.Unmarshal(keyjson, &m); err != nil { + return nil, err + } + // Depending on the version try to parse one way or another + var ( + keyBytes, keyID []byte + err error + ) + if version, ok := m["version"].(string); ok && version == "1" { + k := new(encryptedKeyJSONV1) + if err := json.Unmarshal(keyjson, k); err != nil { + return nil, err + } + keyBytes, keyID, err = decryptKeyV1(k, auth) + } else { + k := new(encryptedKeyJSONV3) + if err := json.Unmarshal(keyjson, k); err != nil { + return nil, err + } + keyBytes, keyID, err = decryptKeyV3(k, auth) + } + // Handle any decryption errors and return the key + if err != nil { + return nil, err + } + key := crypto.ToECDSAUnsafe(keyBytes) + + return &Key{ + ID: uuid.UUID(keyID), + Address: address.PubkeyToAddress(key.PublicKey), + PrivateKey: key, + }, nil +} + +// DecryptDataV3 ... +func DecryptDataV3(cj CryptoJSON, auth string) ([]byte, error) { + if cj.Cipher != "aes-128-ctr" { + return nil, fmt.Errorf("Cipher not supported: %v", cj.Cipher) + } + mac, err := hex.DecodeString(cj.MAC) + if err != nil { + return nil, err + } + + iv, err := hex.DecodeString(cj.CipherParams.IV) + if err != nil { + return nil, err + } + + cipherText, err := hex.DecodeString(cj.CipherText) + if err != nil { + return nil, err + } + + derivedKey, err := getKDFKey(cj, auth) + if err != nil { + return nil, err + } + + calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) + if !bytes.Equal(calculatedMAC, mac) { + return nil, ErrDecrypt + } + + plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv) + if err != nil { + return nil, err + } + return plainText, err +} + +func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyID []byte, err error) { + if keyProtected.Version != version { + return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version) + } + keyID = uuid.Parse(keyProtected.ID) + plainText, err := DecryptDataV3(keyProtected.Crypto, auth) + if err != nil { + return nil, nil, err + } + return plainText, keyID, err +} + +func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyID []byte, err error) { + keyID = uuid.Parse(keyProtected.ID) + mac, err := hex.DecodeString(keyProtected.Crypto.MAC) + if err != nil { + return nil, nil, err + } + + iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV) + if err != nil { + return nil, nil, err + } + + cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) + if err != nil { + return nil, nil, err + } + + derivedKey, err := getKDFKey(keyProtected.Crypto, auth) + if err != nil { + return nil, nil, err + } + + calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) + if !bytes.Equal(calculatedMAC, mac) { + return nil, nil, ErrDecrypt + } + + plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv) + if err != nil { + return nil, nil, err + } + return plainText, keyID, err +} + +func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) { + authArray := []byte(auth) + salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string)) + if err != nil { + return nil, err + } + dkLen := ensureInt(cryptoJSON.KDFParams["dklen"]) + + if cryptoJSON.KDF == keyHeaderKDF { + n := ensureInt(cryptoJSON.KDFParams["n"]) + r := ensureInt(cryptoJSON.KDFParams["r"]) + p := ensureInt(cryptoJSON.KDFParams["p"]) + return scrypt.Key(authArray, salt, n, r, p, dkLen) + + } else if cryptoJSON.KDF == "pbkdf2" { + c := ensureInt(cryptoJSON.KDFParams["c"]) + prf := cryptoJSON.KDFParams["prf"].(string) + if prf != "hmac-sha256" { + return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf) + } + key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New) + return key, nil + } + + return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF) +} + +// TODO: can we do without this when unmarshalling dynamic JSON? +// why do integers in KDF params end up as float64 and not int after +// unmarshal? +func ensureInt(x interface{}) int { + res, ok := x.(int) + if !ok { + res = int(x.(float64)) + } + return res +} diff --git a/pkg/keystore/plain.go b/pkg/keystore/plain.go new file mode 100644 index 00000000..6179d69a --- /dev/null +++ b/pkg/keystore/plain.go @@ -0,0 +1,61 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package keystore + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + + "github.com/fbsobreira/gotron-sdk/pkg/address" +) + +type keyStorePlain struct { + keysDirPath string +} + +func (ks keyStorePlain) GetKey(addr address.Address, filename, auth string) (*Key, error) { + fd, err := os.Open(filename) + if err != nil { + return nil, err + } + defer fd.Close() + key := new(Key) + if err := json.NewDecoder(fd).Decode(key); err != nil { + return nil, err + } + if key.Address != addr { + return nil, fmt.Errorf("key content mismatch: have address %x, want %x", key.Address, addr) + } + return key, nil +} + +func (ks keyStorePlain) StoreKey(filename string, key *Key, auth string) error { + content, err := json.Marshal(key) + if err != nil { + return err + } + return writeKeyFile(filename, content) +} + +func (ks keyStorePlain) JoinPath(filename string) string { + if filepath.IsAbs(filename) { + return filename + } + return filepath.Join(ks.keysDirPath, filename) +} diff --git a/pkg/keystore/store.go b/pkg/keystore/store.go new file mode 100644 index 00000000..8e90b3cb --- /dev/null +++ b/pkg/keystore/store.go @@ -0,0 +1,6 @@ +package keystore + +// ForPath return keystore from path +func ForPath(p string) *KeyStore { + return NewKeyStore(p, StandardScryptN, StandardScryptP) +} diff --git a/pkg/keystore/url.go b/pkg/keystore/url.go new file mode 100644 index 00000000..cc7fe681 --- /dev/null +++ b/pkg/keystore/url.go @@ -0,0 +1,104 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package keystore + +import ( + "encoding/json" + "errors" + "fmt" + "strings" +) + +// URL represents the canonical identification URL of a wallet or account. +// +// It is a simplified version of url.URL, with the important limitations (which +// are considered features here) that it contains value-copyable components only, +// as well as that it doesn't do any URL encoding/decoding of special characters. +// +// The former is important to allow an account to be copied without leaving live +// references to the original version, whereas the latter is important to ensure +// one single canonical form opposed to many allowed ones by the RFC 3986 spec. +// +// As such, these URLs should not be used outside of the scope of an Ethereum +// wallet or account. +type URL struct { + Scheme string // Protocol scheme to identify a capable account backend + Path string // Path for the backend to identify a unique entity +} + +// parseURL converts a user supplied URL into the accounts specific structure. +func parseURL(url string) (URL, error) { + parts := strings.Split(url, "://") + if len(parts) != 2 || parts[0] == "" { + return URL{}, errors.New("protocol scheme missing") + } + return URL{ + Scheme: parts[0], + Path: parts[1], + }, nil +} + +// String implements the stringer interface. +func (u URL) String() string { + if u.Scheme != "" { + return fmt.Sprintf("%s://%s", u.Scheme, u.Path) + } + return u.Path +} + +// TerminalString implements the log.TerminalStringer interface. +func (u URL) TerminalString() string { + url := u.String() + if len(url) > 32 { + return url[:31] + "…" + } + return url +} + +// MarshalJSON implements the json.Marshaller interface. +func (u URL) MarshalJSON() ([]byte, error) { + return json.Marshal(u.String()) +} + +// UnmarshalJSON parses url. +func (u *URL) UnmarshalJSON(input []byte) error { + var textURL string + err := json.Unmarshal(input, &textURL) + if err != nil { + return err + } + url, err := parseURL(textURL) + if err != nil { + return err + } + u.Scheme = url.Scheme + u.Path = url.Path + return nil +} + +// Cmp compares x and y and returns: +// +// -1 if x < y +// 0 if x == y +// +1 if x > y +// +func (u URL) Cmp(url URL) int { + if u.Scheme == url.Scheme { + return strings.Compare(u.Path, url.Path) + } + return strings.Compare(u.Scheme, url.Scheme) +} diff --git a/pkg/keystore/wallet.go b/pkg/keystore/wallet.go new file mode 100644 index 00000000..627656bb --- /dev/null +++ b/pkg/keystore/wallet.go @@ -0,0 +1,139 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package keystore + +import ( + "github.com/ethereum/go-ethereum/crypto" + "github.com/fbsobreira/gotron-sdk/pkg/proto/core" +) + +// keystoreWallet implements the Wallet interface for the original +// keystore. +type keystoreWallet struct { + account Account // Single account contained in this wallet + keystore *KeyStore // Keystore where the account originates from +} + +// URL implements Wallet, returning the URL of the account within. +func (w *keystoreWallet) URL() URL { + return w.account.URL +} + +// Status implements Wallet, returning whether the account held by the +// keystore wallet is unlocked or not. +func (w *keystoreWallet) Status() (string, error) { + w.keystore.mu.RLock() + defer w.keystore.mu.RUnlock() + + if _, ok := w.keystore.unlocked[w.account.Address]; ok { + return "Unlocked", nil + } + return "Locked", nil +} + +// Open implements Wallet, but is a noop for plain wallets since there +// is no connection or decryption step necessary to access the list of account. +func (w *keystoreWallet) Open(passphrase string) error { return nil } + +// Close implements Wallet, but is a noop for plain wallets since there +// is no meaningful open operation. +func (w *keystoreWallet) Close() error { return nil } + +// Accounts implements Wallet, returning an account list consisting of +// a single account that the plain kestore wallet contains. +func (w *keystoreWallet) Accounts() []Account { + return []Account{w.account} +} + +// Contains implements Wallet, returning whether a particular account is +// or is not wrapped by this wallet instance. +func (w *keystoreWallet) Contains(account Account) bool { + return account.Address == w.account.Address && (account.URL == (URL{}) || account.URL == w.account.URL) +} + +// Derive implements Wallet, but is a noop for plain wallets since there +// is no notion of hierarchical account derivation for plain keystore account. +func (w *keystoreWallet) Derive(path DerivationPath, pin bool) (Account, error) { + return Account{}, ErrNotSupported +} + +// signHash attempts to sign the given hash with +// the given account. If the wallet does not wrap this particular account, an +// error is returned to avoid account leakage (even though in theory we may be +// able to sign via our shared keystore backend). +func (w *keystoreWallet) signHash(acc Account, hash []byte) ([]byte, error) { + // Make sure the requested account is contained within + if !w.Contains(acc) { + return nil, ErrUnknownAccount + } + // Account seems valid, request the keystore to sign + return w.keystore.SignHash(acc, hash) +} + +// SignData signs keccak256(data). The mimetype parameter describes the type of data being signed +func (w *keystoreWallet) SignData(acc Account, mimeType string, data []byte) ([]byte, error) { + return w.signHash(acc, crypto.Keccak256(data)) +} + +// SignDataWithPassphrase signs keccak256(data). The mimetype parameter describes the type of data being signed +func (w *keystoreWallet) SignDataWithPassphrase(acc Account, passphrase, mimeType string, data []byte) ([]byte, error) { + // Make sure the requested account is contained within + if !w.Contains(acc) { + return nil, ErrUnknownAccount + } + // Account seems valid, request the keystore to sign + return w.keystore.SignHashWithPassphrase(acc, passphrase, crypto.Keccak256(data)) +} + +func (w *keystoreWallet) SignText(acc Account, text []byte) ([]byte, error) { + return w.signHash(acc, TextHash(text)) +} + +// SignTextWithPassphrase implements Wallet, attempting to sign the +// given hash with the given account using passphrase as extra authentication. +func (w *keystoreWallet) SignTextWithPassphrase(acc Account, passphrase string, text []byte) ([]byte, error) { + // Make sure the requested account is contained within + if !w.Contains(acc) { + return nil, ErrUnknownAccount + } + // Account seems valid, request the keystore to sign + return w.keystore.SignHashWithPassphrase(acc, passphrase, TextHash(text)) +} + +// SignTx implements Wallet, attempting to sign the given transaction +// with the given account. If the wallet does not wrap this particular account, +// an error is returned to avoid account leakage (even though in theory we may +// be able to sign via our shared keystore backend). +func (w *keystoreWallet) SignTx(acc Account, tx *core.Transaction) (*core.Transaction, error) { + // Make sure the requested account is contained within + if !w.Contains(acc) { + return nil, ErrUnknownAccount + } + // Account seems valid, request the keystore to sign + return w.keystore.SignTx(acc, tx) +} + +// SignTxWithPassphrase implements Wallet, attempting to sign the given +// transaction with the given account using passphrase as extra authentication. +func (w *keystoreWallet) SignTxWithPassphrase(acc Account, passphrase string, tx *core.Transaction) (*core.Transaction, error) { + // Make sure the requested account is contained within + if !w.Contains(acc) { + return nil, ErrUnknownAccount + } + // Account seems valid, request the keystore to sign + return w.keystore.SignTxWithPassphrase(acc, passphrase, tx) +} diff --git a/pkg/keystore/watch.go b/pkg/keystore/watch.go new file mode 100644 index 00000000..bbcfb992 --- /dev/null +++ b/pkg/keystore/watch.go @@ -0,0 +1,108 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build darwin,!ios freebsd linux,!arm64 netbsd solaris + +package keystore + +import ( + "time" + + "github.com/ethereum/go-ethereum/log" + "github.com/rjeczalik/notify" +) + +type watcher struct { + ac *accountCache + starting bool + running bool + ev chan notify.EventInfo + quit chan struct{} +} + +func newWatcher(ac *accountCache) *watcher { + return &watcher{ + ac: ac, + ev: make(chan notify.EventInfo, 10), + quit: make(chan struct{}), + } +} + +// starts the watcher loop in the background. +// Start a watcher in the background if that's not already in progress. +// The caller must hold w.ac.mu. +func (w *watcher) start() { + if w.starting || w.running { + return + } + w.starting = true + go w.loop() +} + +func (w *watcher) close() { + close(w.quit) +} + +func (w *watcher) loop() { + defer func() { + w.ac.mu.Lock() + w.running = false + w.starting = false + w.ac.mu.Unlock() + }() + logger := log.New("path", w.ac.keydir) + + if err := notify.Watch(w.ac.keydir, w.ev, notify.All); err != nil { + logger.Trace("Failed to watch keystore folder", "err", err) + return + } + defer notify.Stop(w.ev) + logger.Trace("Started watching keystore folder") + defer logger.Trace("Stopped watching keystore folder") + + w.ac.mu.Lock() + w.running = true + w.ac.mu.Unlock() + + // Wait for file system events and reload. + // When an event occurs, the reload call is delayed a bit so that + // multiple events arriving quickly only cause a single reload. + var ( + debounceDuration = 500 * time.Millisecond + rescanTriggered = false + debounce = time.NewTimer(0) + ) + // Ignore initial trigger + if !debounce.Stop() { + <-debounce.C + } + defer debounce.Stop() + for { + select { + case <-w.quit: + return + case <-w.ev: + // Trigger the scan (with delay), if not already triggered + if !rescanTriggered { + debounce.Reset(debounceDuration) + rescanTriggered = true + } + case <-debounce.C: + w.ac.scanAccounts() + rescanTriggered = false + } + } +} diff --git a/pkg/keystore/watch_fallback.go b/pkg/keystore/watch_fallback.go new file mode 100644 index 00000000..7c5e9cb2 --- /dev/null +++ b/pkg/keystore/watch_fallback.go @@ -0,0 +1,28 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build ios linux,arm64 windows !darwin,!freebsd,!linux,!netbsd,!solaris + +// This is the fallback implementation of directory watching. +// It is used on unsupported platforms. + +package keystore + +type watcher struct{ running bool } + +func newWatcher(*accountCache) *watcher { return new(watcher) } +func (*watcher) start() {} +func (*watcher) close() {} diff --git a/pkg/ledger/hw_wallet.go b/pkg/ledger/hw_wallet.go new file mode 100644 index 00000000..744a2904 --- /dev/null +++ b/pkg/ledger/hw_wallet.go @@ -0,0 +1,89 @@ +package ledger + +import ( + "fmt" + "log" + "os" + "sync" + + "golang.org/x/crypto/sha3" + + "github.com/ethereum/go-ethereum/crypto" +) + +var ( + nanos *NanoS //singleton + once sync.Once +) + +func getLedger() *NanoS { + once.Do(func() { + var err error + nanos, err = OpenNanoS() + if err != nil { + log.Fatalln("Couldn't open device:", err) + os.Exit(-1) + } + }) + + return nanos +} + +// GetAddress ProcessAddressCommand list the address associated with Ledger Nano S +func GetAddress() string { + n := getLedger() + addr, err := n.GetAddress() + if err != nil { + log.Fatalln("Couldn't get address:", err) + os.Exit(-1) + } + + return addr +} + +//ProcessAddressCommand list the address associated with Ledger Nano S +func ProcessAddressCommand() { + n := getLedger() + addr, err := n.GetAddress() + if err != nil { + log.Fatalln("Couldn't get address:", err) + os.Exit(-1) + } + + fmt.Printf("%-24s\t\t%23s\n", "NAME", "ADDRESS") + fmt.Printf("%-48s\t%s\n", "Ledger Nano S", addr) +} + +// SignTx signs the given transaction with the requested account. +func SignTx(tx []byte) ([]byte, error) { + + n := getLedger() + sig, err := n.SignTxn(tx) + if err != nil { + log.Println("Couldn't sign transaction, error:", err) + return nil, err + } + + var hashBytes [32]byte + hw := sha3.NewLegacyKeccak256() + hw.Write(tx[:]) + hw.Sum(hashBytes[:0]) + + pubkey, err := crypto.Ecrecover(hashBytes[:], sig[:]) + if err != nil { + log.Println("Ecrecover failed :", err) + return nil, err + } + + if len(pubkey) == 0 || pubkey[0] != 4 { + log.Println("invalid public key") + return nil, err + } + + //pubBytes := crypto.Keccak256(pubkey[1:65])[12:] + //signerAddr, _ := address.PubkeyToAddress(pubBytes) + + // TODO: + //return sig, nil + return nil, nil +} diff --git a/pkg/ledger/nano_S_driver.go b/pkg/ledger/nano_S_driver.go new file mode 100644 index 00000000..f2112571 --- /dev/null +++ b/pkg/ledger/nano_S_driver.go @@ -0,0 +1,262 @@ +package ledger + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "errors" + "fmt" + "io" + + "github.com/karalabe/hid" +) + +const ( + signatureSize int = 65 + packetSize int = 255 +) + +var DEBUG bool + +type hidFramer struct { + rw io.ReadWriter + seq uint16 + buf [64]byte + pos int +} + +type APDU struct { + CLA byte + INS byte + P1, P2 byte + Payload []byte +} + +type apduFramer struct { + hf *hidFramer + buf [2]byte // to read APDU length prefix +} + +type NanoS struct { + device *apduFramer +} + +type ErrCode uint16 + +func (hf *hidFramer) Reset() { + hf.seq = 0 +} + +func (hf *hidFramer) Write(p []byte) (int, error) { + if DEBUG { + fmt.Println("HID <=", hex.EncodeToString(p)) + } + // split into 64-byte chunks + chunk := make([]byte, 64) + binary.BigEndian.PutUint16(chunk[:2], 0x0101) + chunk[2] = 0x05 + var seq uint16 + buf := new(bytes.Buffer) + binary.Write(buf, binary.BigEndian, uint16(len(p))) + buf.Write(p) + for buf.Len() > 0 { + binary.BigEndian.PutUint16(chunk[3:5], seq) + n, _ := buf.Read(chunk[5:]) + if n, err := hf.rw.Write(chunk[:5+n]); err != nil { + return n, err + } + seq++ + } + return len(p), nil +} + +func (hf *hidFramer) Read(p []byte) (int, error) { + if hf.seq > 0 && hf.pos != 64 { + // drain buf + n := copy(p, hf.buf[hf.pos:]) + hf.pos += n + return n, nil + } + // read next 64-byte packet + if n, err := hf.rw.Read(hf.buf[:]); err != nil { + return 0, err + } else if n != 64 { + panic("read less than 64 bytes from HID") + } + // parse header + channelID := binary.BigEndian.Uint16(hf.buf[:2]) + commandTag := hf.buf[2] + seq := binary.BigEndian.Uint16(hf.buf[3:5]) + if channelID != 0x0101 { + return 0, fmt.Errorf("bad channel ID 0x%x", channelID) + } else if commandTag != 0x05 { + return 0, fmt.Errorf("bad command tag 0x%x", commandTag) + } else if seq != hf.seq { + return 0, fmt.Errorf("bad sequence number %v (expected %v)", seq, hf.seq) + } + hf.seq++ + // start filling p + n := copy(p, hf.buf[5:]) + hf.pos = 5 + n + return n, nil +} + +func (af *apduFramer) Exchange(apdu APDU) ([]byte, error) { + if len(apdu.Payload) > packetSize { + panic("APDU payload cannot exceed 255 bytes") + } + af.hf.Reset() + data := append([]byte{ + apdu.CLA, + apdu.INS, + apdu.P1, apdu.P2, + byte(len(apdu.Payload)), + }, apdu.Payload...) + if _, err := af.hf.Write(data); err != nil { + return nil, err + } + + // read APDU length + if _, err := io.ReadFull(af.hf, af.buf[:]); err != nil { + return nil, err + } + // read APDU payload + respLen := binary.BigEndian.Uint16(af.buf[:2]) + resp := make([]byte, respLen) + _, err := io.ReadFull(af.hf, resp) + if DEBUG { + fmt.Println("HID =>", hex.EncodeToString(resp)) + } + return resp, err +} + +func (c ErrCode) Error() string { + return fmt.Sprintf("Error code 0x%x", uint16(c)) +} + +const codeSuccess = 0x9000 +const codeUserRejected = 0x6985 +const codeInvalidParam = 0x6b01 + +var errUserRejected = errors.New("user denied request") +var errInvalidParam = errors.New("invalid request parameters") + +func (n *NanoS) Exchange(cmd byte, p1, p2 byte, data []byte) (resp []byte, err error) { + resp, err = n.device.Exchange(APDU{ + CLA: 0xe0, + INS: cmd, + P1: p1, + P2: p2, + Payload: data, + }) + if err != nil { + return nil, err + } else if len(resp) < 2 { + return nil, errors.New("APDU response missing status code") + } + code := binary.BigEndian.Uint16(resp[len(resp)-2:]) + resp = resp[:len(resp)-2] + switch code { + case codeSuccess: + err = nil + case codeUserRejected: + err = errUserRejected + case codeInvalidParam: + err = errInvalidParam + default: + err = ErrCode(code) + } + return +} + +const ( + cmdGetVersion = 0x01 + cmdGetPublicKey = 0x02 + cmdSignStaking = 0x04 + cmdSignTx = 0x08 + + p1First = 0x0 + p1More = 0x80 + + p2DisplayAddress = 0x00 + p2DisplayHash = 0x00 + p2SignHash = 0x01 + p2Finish = 0x02 +) + +// GetVersion return app version +func (n *NanoS) GetVersion() (version string, err error) { + resp, err := n.Exchange(cmdGetVersion, 0, 0, nil) + if err != nil { + return "", err + } else if len(resp) != 3 { + return "", errors.New("version has wrong length") + } + return fmt.Sprintf("v%d.%d.%d", resp[0], resp[1], resp[2]), nil +} + +// GetAddress return address from path +func (n *NanoS) GetAddress() (addr string, err error) { + resp, err := n.Exchange(cmdGetPublicKey, 0, p2DisplayAddress, []byte{}) + if err != nil { + return "", err + } + + var pubkey [42]byte + if copy(pubkey[:], resp) != len(pubkey) { + return "", errors.New("pubkey has wrong length") + } + return string(pubkey[:]), nil +} + +// SignTxn sign a TX +func (n *NanoS) SignTxn(txn []byte) (sig [signatureSize]byte, err error) { + var resp []byte + + var p1 byte = p1More + resp, err = n.Exchange(cmdSignTx, p1, p2SignHash, txn) + if err != nil { + return [signatureSize]byte{}, err + } + + copy(sig[:], resp) + + if copy(sig[:], resp) != len(sig) { + return [signatureSize]byte{}, errors.New("signature has wrong length") + } + return +} + +// OpenNanoS start process +func OpenNanoS() (*NanoS, error) { + const ( + ledgerVendorID = 0x2c97 + // new device ID for firmware 1.6.0 + ledgerNanoSProductID = 0x1011 + // ledgerNanoSProductID = 0x0001 + //ledgerUsageID = 0xffa0 + ) + + // search for Nano S + devices := hid.Enumerate(ledgerVendorID, ledgerNanoSProductID) + if len(devices) == 0 { + return nil, errors.New("Nano S not detected") + } else if len(devices) > 1 { + return nil, errors.New("Unexpected error -- Is the one wallet app running?") + } + + // open the device + device, err := devices[0].Open() + if err != nil { + return nil, err + } + + // wrap raw device I/O in HID+APDU protocols + return &NanoS{ + device: &apduFramer{ + hf: &hidFramer{ + rw: device, + }, + }, + }, nil +} diff --git a/pkg/mnemonic/mnemonic.go b/pkg/mnemonic/mnemonic.go new file mode 100644 index 00000000..4b16c336 --- /dev/null +++ b/pkg/mnemonic/mnemonic.go @@ -0,0 +1,19 @@ +package mnemonic + +import ( + "fmt" + + "github.com/tyler-smith/go-bip39" +) + +var ( + // ErrInvalidMnemonic error + ErrInvalidMnemonic = fmt.Errorf("invalid mnemonic given") +) + +// Generate with 24 words deafult +func Generate() string { + entropy, _ := bip39.NewEntropy(256) + mnemonic, _ := bip39.NewMnemonic(entropy) + return mnemonic +} diff --git a/api/api.pb.go b/pkg/proto/api/api.pb.go similarity index 99% rename from api/api.pb.go rename to pkg/proto/api/api.pb.go index 84d91ef2..d5ead60d 100644 --- a/api/api.pb.go +++ b/pkg/proto/api/api.pb.go @@ -6,13 +6,14 @@ package api import ( context "context" fmt "fmt" - core "github.com/fbsobreira/gotron/core" + math "math" + + core "github.com/fbsobreira/gotron-sdk/pkg/proto/core" proto "github.com/golang/protobuf/proto" _ "google.golang.org/genproto/googleapis/api/annotations" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" - math "math" ) // Reference imports to suppress errors if they are not otherwise used. diff --git a/api/zksnark.pb.go b/pkg/proto/api/zksnark.pb.go similarity index 99% rename from api/zksnark.pb.go rename to pkg/proto/api/zksnark.pb.go index 1423249a..bc4aaa06 100644 --- a/api/zksnark.pb.go +++ b/pkg/proto/api/zksnark.pb.go @@ -6,12 +6,13 @@ package api import ( context "context" fmt "fmt" - core "github.com/fbsobreira/gotron/core" + math "math" + + core "github.com/fbsobreira/gotron-sdk/pkg/proto/core" proto "github.com/golang/protobuf/proto" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" - math "math" ) // Reference imports to suppress errors if they are not otherwise used. diff --git a/core/Discover.pb.go b/pkg/proto/core/Discover.pb.go similarity index 100% rename from core/Discover.pb.go rename to pkg/proto/core/Discover.pb.go diff --git a/core/Tron.pb.go b/pkg/proto/core/Tron.pb.go similarity index 99% rename from core/Tron.pb.go rename to pkg/proto/core/Tron.pb.go index 6f390e31..63034393 100644 --- a/core/Tron.pb.go +++ b/pkg/proto/core/Tron.pb.go @@ -5,9 +5,10 @@ package core import ( fmt "fmt" + math "math" + proto "github.com/golang/protobuf/proto" any "github.com/golang/protobuf/ptypes/any" - math "math" ) // Reference imports to suppress errors if they are not otherwise used. diff --git a/core/TronInventoryItems.pb.go b/pkg/proto/core/TronInventoryItems.pb.go similarity index 100% rename from core/TronInventoryItems.pb.go rename to pkg/proto/core/TronInventoryItems.pb.go diff --git a/core/account_contract.pb.go b/pkg/proto/core/account_contract.pb.go similarity index 100% rename from core/account_contract.pb.go rename to pkg/proto/core/account_contract.pb.go diff --git a/core/asset_issue_contract.pb.go b/pkg/proto/core/asset_issue_contract.pb.go similarity index 100% rename from core/asset_issue_contract.pb.go rename to pkg/proto/core/asset_issue_contract.pb.go diff --git a/core/balance_contract.pb.go b/pkg/proto/core/balance_contract.pb.go similarity index 100% rename from core/balance_contract.pb.go rename to pkg/proto/core/balance_contract.pb.go diff --git a/core/common.pb.go b/pkg/proto/core/common.pb.go similarity index 100% rename from core/common.pb.go rename to pkg/proto/core/common.pb.go diff --git a/core/exchange_contract.pb.go b/pkg/proto/core/exchange_contract.pb.go similarity index 100% rename from core/exchange_contract.pb.go rename to pkg/proto/core/exchange_contract.pb.go diff --git a/core/proposal_contract.pb.go b/pkg/proto/core/proposal_contract.pb.go similarity index 100% rename from core/proposal_contract.pb.go rename to pkg/proto/core/proposal_contract.pb.go diff --git a/core/shield_contract.pb.go b/pkg/proto/core/shield_contract.pb.go similarity index 100% rename from core/shield_contract.pb.go rename to pkg/proto/core/shield_contract.pb.go diff --git a/core/smart_contract.pb.go b/pkg/proto/core/smart_contract.pb.go similarity index 100% rename from core/smart_contract.pb.go rename to pkg/proto/core/smart_contract.pb.go diff --git a/core/storage_contract.pb.go b/pkg/proto/core/storage_contract.pb.go similarity index 100% rename from core/storage_contract.pb.go rename to pkg/proto/core/storage_contract.pb.go diff --git a/core/vote_asset_contract.pb.go b/pkg/proto/core/vote_asset_contract.pb.go similarity index 100% rename from core/vote_asset_contract.pb.go rename to pkg/proto/core/vote_asset_contract.pb.go diff --git a/core/witness_contract.pb.go b/pkg/proto/core/witness_contract.pb.go similarity index 100% rename from core/witness_contract.pb.go rename to pkg/proto/core/witness_contract.pb.go diff --git a/pkg/store/local.go b/pkg/store/local.go new file mode 100644 index 00000000..cfba14fd --- /dev/null +++ b/pkg/store/local.go @@ -0,0 +1,128 @@ +package store + +import ( + "fmt" + "io/ioutil" + "os" + "path" + + "github.com/fbsobreira/gotron-sdk/pkg/address" + "github.com/fbsobreira/gotron-sdk/pkg/common" + c "github.com/fbsobreira/gotron-sdk/pkg/common" + "github.com/fbsobreira/gotron-sdk/pkg/keystore" + "github.com/pkg/errors" + + homedir "github.com/mitchellh/go-homedir" +) + +func init() { + uDir, _ := homedir.Dir() + tronCTLDir := path.Join(uDir, common.DefaultConfigDirName, common.DefaultConfigAccountAliasesDirName) + if _, err := os.Stat(tronCTLDir); os.IsNotExist(err) { + os.MkdirAll(tronCTLDir, 0700) + } +} + +// LocalAccounts returns a slice of local account alias names +func LocalAccounts() []string { + uDir, _ := homedir.Dir() + files, _ := ioutil.ReadDir(path.Join( + uDir, + common.DefaultConfigDirName, + common.DefaultConfigAccountAliasesDirName, + )) + accounts := []string{} + + for _, node := range files { + if node.IsDir() { + accounts = append(accounts, path.Base(node.Name())) + } + } + return accounts +} + +var ( + describe = fmt.Sprintf("%-24s\t\t%23s\n", "NAME", "ADDRESS") + // ErrNoUnlockBadPassphrase for bad password + ErrNoUnlockBadPassphrase = fmt.Errorf("could not unlock account with passphrase, perhaps need different phrase") +) + +// DescribeLocalAccounts will display all the account alias name and their corresponding one address +func DescribeLocalAccounts() { + fmt.Println(describe) + for _, name := range LocalAccounts() { + ks := FromAccountName(name) + allAccounts := ks.Accounts() + for _, account := range allAccounts { + fmt.Printf("%-48s\t%s\n", name, account.Address) + } + } +} + +// DoesNamedAccountExist return true if the given string name is an alias account already define, +// and return false otherwise +func DoesNamedAccountExist(name string) bool { + for _, account := range LocalAccounts() { + if account == name { + return true + } + } + return false +} + +// AddressFromAccountName Returns address for account name if exists +func AddressFromAccountName(name string) (string, error) { + ks := FromAccountName(name) + // FIXME: Assume 1 account per keystore for now + for _, account := range ks.Accounts() { + return account.Address.String(), nil + } + return "", fmt.Errorf("keystore not found") +} + +// FromAddress will return nil if the Base58 string is not found in the imported accounts +func FromAddress(addr string) *keystore.KeyStore { + for _, name := range LocalAccounts() { + ks := FromAccountName(name) + allAccounts := ks.Accounts() + for _, account := range allAccounts { + if addr == account.Address.String() { + return ks + } + } + } + return nil +} + +// FromAccountName get account from name +func FromAccountName(name string) *keystore.KeyStore { + uDir, _ := homedir.Dir() + p := path.Join(uDir, c.DefaultConfigDirName, c.DefaultConfigAccountAliasesDirName, name) + return keystore.ForPath(p) +} + +// DefaultLocation get deafault location +func DefaultLocation() string { + uDir, _ := homedir.Dir() + return path.Join(uDir, c.DefaultConfigDirName, c.DefaultConfigAccountAliasesDirName) +} + +// UnlockedKeystore return keystore unlocked +func UnlockedKeystore(from, passphrase string) (*keystore.KeyStore, *keystore.Account, error) { + sender, err := address.Base58ToAddress(from) + if err != nil { + return nil, nil, fmt.Errorf("address not valid: %s", from) + } + ks := FromAddress(from) + if ks == nil { + return nil, nil, fmt.Errorf("could not open local keystore for %s", from) + } + account, lookupErr := ks.Find(keystore.Account{Address: sender}) + if lookupErr != nil { + return nil, nil, fmt.Errorf("could not find %s in keystore", from) + } + if unlockError := ks.Unlock(account, passphrase); unlockError != nil { + return nil, nil, errors.Wrap(ErrNoUnlockBadPassphrase, unlockError.Error()) + } + return ks, &account, nil +} diff --git a/program/createaccount.go b/program/createaccount.go deleted file mode 100644 index 41fa6d06..00000000 --- a/program/createaccount.go +++ /dev/null @@ -1,44 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "github.com/fbsobreira/gotron/common/crypto" - "github.com/fbsobreira/gotron/service" - "log" - "strings" -) - -func main() { - grpcAddress := flag.String("grpcAddress", "", - "gRPC address: example: -grpcAddress localhost:50051") - - ownerPrivateKey := flag.String("ownerPrivateKey", "", - "ownerPrivateKey: ") - - address := flag.String("newAccountAddress", "", - "newAccountAddress: ") - - flag.Parse() - - if (strings.EqualFold("", *ownerPrivateKey) && len(*ownerPrivateKey) == 0) || (strings. - EqualFold("", *address) && len(*address) == 0) || (strings.EqualFold("", *grpcAddress) && len(*grpcAddress) == 0) { - log.Fatalln("./create-account -grpcAddress localhost" + - ":50051 -ownerPrivateKey -newAccountAddress" + - " ") - } - - client := service.NewGrpcClient(*grpcAddress) - client.Start() - defer client.Conn.Close() - - key, err := crypto.GetPrivateKeyByHexString(*ownerPrivateKey) - - if err != nil { - log.Fatalf("get private key by hex string error: %v", err) - } - - result := client.CreateAccount(key, *address) - - fmt.Printf("result: %v\n", result) -} diff --git a/program/createassetissue.go b/program/createassetissue.go deleted file mode 100644 index 2866f89a..00000000 --- a/program/createassetissue.go +++ /dev/null @@ -1,116 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "log" - "strings" - - "github.com/fbsobreira/gotron/common/crypto" - "github.com/fbsobreira/gotron/service" -) - -func main() { - grpcAddress := flag.String("grpcAddress", "", - "gRPC address: example: -grpcAddress localhost:50051") - - ownerPrivateKey := flag.String("ownerPrivateKey", "", - "ownerPrivateKey: ") - - name := flag.String("name", "", - "name: ") - - description := flag.String("description", "", - "description: ") - - abbr := flag.String("abbr", "", - "description: ") - - urlStr := flag.String("url", "", - "url: ") - - totalSupply := flag.Int64("totalSupply", 0, - "totalSupply: ") - - startTime := flag.Int64("startTime", 0, - "startTime: ") - - endTime := flag.Int64("endTime", 0, - "endTime: ") - - freeAssetNetLimit := flag.Int64("freeAssetNetLimit", 0, - "freeAssetNetLimit: ") - - publicFreeAssetNetLimit := flag.Int64("publicFreeAssetNetLimit", 0, - "publicFreeAssetNetLimit: ") - - trxNum := flag.Int("trxNum", 0, - "trxNum: ") - - icoNum := flag.Int("icoNum", 0, - "icoNum: ") - - frozenSupply := flag.String("frozenSupply", "", - "frozenSupply: ") - - flag.Parse() - - if (strings.EqualFold("", *ownerPrivateKey) && len(*ownerPrivateKey) == 0) || - (strings.EqualFold("", *name) && len(*name) == 0) || - (strings.EqualFold("", *grpcAddress) && len(*grpcAddress) == 0) || - (strings.EqualFold("", *description) && len(*description) == 0) || - (strings.EqualFold("", *abbr) && len(*abbr) > 4) || - (strings.EqualFold("", *abbr) && len(*abbr) == 0) || - (strings.EqualFold("", *urlStr) && len(*urlStr) == 0) || - (*totalSupply <= 0) || - (*startTime <= 0) || - (*endTime <= 0) || - (*freeAssetNetLimit < 0) || - (*publicFreeAssetNetLimit < 0) || - (*trxNum <= 0) || - (*icoNum <= 0) || - (strings.EqualFold("", *frozenSupply) && len(*frozenSupply) == 0) { - log.Fatalln("./create-asset-issue " + - "-grpcAddress localhost:50051 " + - "-ownerPrivateKey " + - "-name " + - "-description " + - "-abbr " + - "-url " + - "-totalSupply " + - "-startTime " + - "-endTime " + - "-freeAssetNetLimit " + - "-publicFreeAssetNetLimit " + - "-trxNum " + - "-icoNum " + - "-frozenSupply ") - } - - frozenSupplySlice := strings.Split(*frozenSupply, ",") - - frozenSupplyMap := make(map[string]string) - for _, value := range frozenSupplySlice { - frozenSupplyKeyValue := strings.Split(value, ":") - frozenSupplyMap[frozenSupplyKeyValue[0]] = frozenSupplyKeyValue[1] - } - - client := service.NewGrpcClient(*grpcAddress) - client.Start() - defer client.Conn.Close() - - key, err := crypto.GetPrivateKeyByHexString(*ownerPrivateKey) - - if err != nil { - log.Fatalf("get private key by hex string error: %v", err) - } - - result := client.CreateAssetIssue(key, *name, *description, *abbr, *urlStr, - *totalSupply, *startTime, *endTime, *freeAssetNetLimit, - *publicFreeAssetNetLimit, int32(*trxNum), int32(*icoNum), 0, - frozenSupplyMap) - - fmt.Printf("result: %v\n", result) -} diff --git a/program/freezebalance.go b/program/freezebalance.go deleted file mode 100644 index 12cc016c..00000000 --- a/program/freezebalance.go +++ /dev/null @@ -1,47 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "github.com/fbsobreira/gotron/common/crypto" - "github.com/fbsobreira/gotron/service" - "log" - "strings" -) - -const frozenDuration = 3 - -func main() { - grpcAddress := flag.String("grpcAddress", "", - "gRPC address: example: -grpcAddress localhost:50051") - - ownerPrivateKey := flag.String("ownerPrivateKey", "", - "ownerPrivateKey: ") - - frozenBalance := flag.Int64("frozenBalance", 0, - "frozenBalance: ") - - flag.Parse() - - if (strings.EqualFold("", *ownerPrivateKey) && len( - *ownerPrivateKey) == 0) || (*frozenBalance <= 0) || (strings.EqualFold( - "", *grpcAddress) && len(*grpcAddress) == 0) { - log.Fatalln("./freeze-balance -grpcAddress localhost" + - ":50051 -ownerPrivateKey -frozenBalance" + - " ") - } - - client := service.NewGrpcClient(*grpcAddress) - client.Start() - defer client.Conn.Close() - - key, err := crypto.GetPrivateKeyByHexString(*ownerPrivateKey) - - if err != nil { - log.Fatalf("get private key by hex string error: %v", err) - } - - result := client.FreezeBalance(key, *frozenBalance, frozenDuration) - - fmt.Printf("result: %v\n", result) -} diff --git a/program/getaccountbyaddress.go b/program/getaccountbyaddress.go deleted file mode 100644 index c5c6c5b0..00000000 --- a/program/getaccountbyaddress.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "github.com/fbsobreira/gotron/common/hexutil" - "github.com/fbsobreira/gotron/service" - "log" - "strings" -) - -func main() { - grpcAddress := flag.String("grpcAddress", "", - "gRPC address: example: -grpcAddress localhost:50051") - - address := flag.String("address", "", - "address: ") - - flag.Parse() - - if (strings.EqualFold("", *address) && len(*address) == 0) || (strings.EqualFold("", *grpcAddress) && len(*grpcAddress) == 0) { - log.Fatalln("./get-account-by-address -grpcAddress localhost" + - ":50051 -address ") - } - - client := service.NewGrpcClient(*grpcAddress) - client.Start() - defer client.Conn.Close() - - account := client.GetAccount(*address) - - fmt.Printf("account: type: %s, address: %s, balance: %d\n", account.Type, - hexutil.Encode(account.Address), account.Balance) -} diff --git a/program/getaccountnet.go b/program/getaccountnet.go deleted file mode 100644 index 4251b958..00000000 --- a/program/getaccountnet.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "github.com/fbsobreira/gotron/service" - "log" - "strings" -) - -func main() { - grpcAddress := flag.String("grpcAddress", "", - "gRPC address: example: -grpcAddress localhost:50051") - - address := flag.String("address", "", - "address: ") - - flag.Parse() - - if (strings.EqualFold("", *address) && len(*address) == 0) || (strings.EqualFold("", *grpcAddress) && len(*grpcAddress) == 0) { - log.Fatalln("./get-account-net -grpcAddress localhost" + - ":50051 -address ") - } - - client := service.NewGrpcClient(*grpcAddress) - client.Start() - defer client.Conn.Close() - - accountNet := client.GetAccountNet(*address) - - fmt.Printf("account net: %v\n", accountNet) -} diff --git a/program/getassetissuebyaccount.go b/program/getassetissuebyaccount.go deleted file mode 100644 index 3b1d1131..00000000 --- a/program/getassetissuebyaccount.go +++ /dev/null @@ -1,33 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "github.com/fbsobreira/gotron/service" - "log" - "strings" -) - -func main() { - grpcAddress := flag.String("grpcAddress", "", - "gRPC address: example: -grpcAddress localhost:50051") - - address := flag.String("address", "", - "address: ") - - flag.Parse() - - if (strings.EqualFold("", *address) && len(*address) == 0) || (strings.EqualFold("", *grpcAddress) && len(*grpcAddress) == 0) { - log.Fatalln("./get-asset-issue-by-account -grpcAddress localhost" + - ":50051 -address ") - } - - client := service.NewGrpcClient(*grpcAddress) - client.Start() - defer client.Conn.Close() - - assetIssueList := client.GetAssetIssueByAccount(*address) - - fmt.Printf("asset issue list: %v\n", - assetIssueList) -} diff --git a/program/getassetissuebyname.go b/program/getassetissuebyname.go deleted file mode 100644 index c93e73cc..00000000 --- a/program/getassetissuebyname.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "github.com/fbsobreira/gotron/service" - "log" - "strings" -) - -func main() { - grpcAddress := flag.String("grpcAddress", "", - "gRPC address: example: -grpcAddress localhost:50051") - - assetName := flag.String("assetName", "", - "assetName: ") - - flag.Parse() - - if (strings.EqualFold("", *assetName) && len(*assetName) == 0) || (strings.EqualFold("", *grpcAddress) && len(*grpcAddress) == 0) { - log.Fatalln("./get-asset-issue-by-name -grpcAddress localhost" + - ":50051 -assetName ") - } - - client := service.NewGrpcClient(*grpcAddress) - client.Start() - defer client.Conn.Close() - - asset := client.GetAssetIssueByName(*assetName) - - fmt.Printf("asset issue: %v\n", asset) -} diff --git a/program/getassetissuelist.go b/program/getassetissuelist.go deleted file mode 100644 index eb6cbae2..00000000 --- a/program/getassetissuelist.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "github.com/fbsobreira/gotron/service" - "log" - "strings" -) - -func main() { - grpcAddress := flag.String("grpcAddress", "", - "gRPC address: example: -grpcAddress localhost:50051") - - flag.Parse() - - if strings.EqualFold("", *grpcAddress) && len(*grpcAddress) == 0 { - log.Fatalln("./get-asset-issue-list -grpcAddress localhost:50051") - } - - client := service.NewGrpcClient(*grpcAddress) - client.Start() - defer client.Conn.Close() - - assetIssueList := client.GetAssetIssueList() - - fmt.Printf("asset issue list: %v\n", assetIssueList) -} diff --git a/program/getblockbyid.go b/program/getblockbyid.go deleted file mode 100644 index 8a048fde..00000000 --- a/program/getblockbyid.go +++ /dev/null @@ -1,33 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "github.com/fbsobreira/gotron/service" - "log" - "strings" -) - -func main() { - grpcAddress := flag.String("grpcAddress", "", - "gRPC address: example: -grpcAddress localhost:50051") - - hash := flag.String("hash", - "", - "hash: ") - - flag.Parse() - - if (strings.EqualFold("", *hash) && len(*hash) == 0) || (strings.EqualFold("", *grpcAddress) && len(*grpcAddress) == 0) { - log.Fatalln("./get-block-by-id -grpcAddress localhost" + - ":50051 -hash ") - } - - client := service.NewGrpcClient(*grpcAddress) - client.Start() - defer client.Conn.Close() - - block := client.GetBlockById(*hash) - - fmt.Printf("block: %v\n", block) -} diff --git a/program/getblockbylatestnum.go b/program/getblockbylatestnum.go deleted file mode 100644 index f142544f..00000000 --- a/program/getblockbylatestnum.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "github.com/fbsobreira/gotron/service" - "log" - "strings" -) - -func main() { - grpcAddress := flag.String("grpcAddress", "", - "gRPC address: example: -grpcAddress localhost:50051") - - num := flag.Int64("number", 0, - "number: ") - - flag.Parse() - - if (*num < 0) || (strings.EqualFold("", *grpcAddress) && len(*grpcAddress) == 0) { - log.Fatalln("./get-block-by-latest-num -grpcAddress localhost" + - ":50051 -number ") - } - - client := service.NewGrpcClient(*grpcAddress) - client.Start() - defer client.Conn.Close() - - blockList := client.GetBlockByLatestNum(*num) - - fmt.Printf("block list: %v\n", blockList) -} diff --git a/program/getblockbylimitnext.go b/program/getblockbylimitnext.go deleted file mode 100644 index dac7018f..00000000 --- a/program/getblockbylimitnext.go +++ /dev/null @@ -1,36 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "github.com/fbsobreira/gotron/service" - "log" - "strings" -) - -func main() { - grpcAddress := flag.String("grpcAddress", "", - "gRPC address: example: -grpcAddress localhost:50051") - - start := flag.Int64("start", 0, - "start: ") - - end := flag.Int64("end", 1, - "end: ") - - flag.Parse() - - if (*start < 0) || (*end < 0) || (strings.EqualFold("", - *grpcAddress) && len(*grpcAddress) == 0) { - log.Fatalln("./get-block-by-limit-next -grpcAddress localhost" + - ":50051 -start -end ") - } - - client := service.NewGrpcClient(*grpcAddress) - client.Start() - defer client.Conn.Close() - - blockList := client.GetBlockByLimitNext(*start, *end) - - fmt.Printf("block list: %v\n", blockList) -} diff --git a/program/getblockbynum.go b/program/getblockbynum.go deleted file mode 100644 index bb1aa704..00000000 --- a/program/getblockbynum.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "github.com/fbsobreira/gotron/service" - "log" - "strings" -) - -func main() { - grpcAddress := flag.String("grpcAddress", "", - "gRPC address: example: -grpcAddress localhost:50051") - - num := flag.Int64("number", 0, - "number: ") - - flag.Parse() - - if (*num < 0) || (strings.EqualFold("", *grpcAddress) && len(*grpcAddress) == 0) { - log.Fatalln("./get-block-by-num -grpcAddress localhost" + - ":50051 -number ") - } - - client := service.NewGrpcClient(*grpcAddress) - client.Start() - defer client.Conn.Close() - - block := client.GetBlockByNum(*num) - - fmt.Printf("block: %v\n", block) -} diff --git a/program/getnextmaintenancetime.go b/program/getnextmaintenancetime.go deleted file mode 100644 index 071f59ab..00000000 --- a/program/getnextmaintenancetime.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "github.com/fbsobreira/gotron/service" - "log" - "strings" -) - -func main() { - grpcAddress := flag.String("grpcAddress", "", - "gRPC address: example: -grpcAddress localhost:50051") - - flag.Parse() - - if strings.EqualFold("", *grpcAddress) && len(*grpcAddress) == 0 { - log.Fatalln("./get-next-maintenance-time -grpcAddress localhost:50051") - } - - client := service.NewGrpcClient(*grpcAddress) - client.Start() - defer client.Conn.Close() - - num := client.GetNextMaintenanceTime() - - fmt.Printf("next maintenance time: %v\n", num) -} diff --git a/program/getnowblock.go b/program/getnowblock.go deleted file mode 100644 index 72c82b78..00000000 --- a/program/getnowblock.go +++ /dev/null @@ -1,33 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "github.com/fbsobreira/gotron/common/hexutil" - "github.com/fbsobreira/gotron/service" - "github.com/fbsobreira/gotron/util" - "log" - "strings" -) - -func main() { - grpcAddress := flag.String("grpcAddress", "", - "gRPC address: example: -grpcAddress localhost:50051") - - flag.Parse() - - if strings.EqualFold("", *grpcAddress) && len(*grpcAddress) == 0 { - log.Fatalln("./get-now-block -grpcAddress localhost:50051") - } - - client := service.NewGrpcClient(*grpcAddress) - client.Start() - defer client.Conn.Close() - - block := client.GetNowBlock() - - blockHash := util.GetBlockHash(*block) - - fmt.Printf("now block: block number: %v, hash: %v\n", - block.BlockHeader.RawData.Number, hexutil.Encode(blockHash)) -} diff --git a/program/gettransactionbyid.go b/program/gettransactionbyid.go deleted file mode 100644 index 1746fc91..00000000 --- a/program/gettransactionbyid.go +++ /dev/null @@ -1,33 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "github.com/fbsobreira/gotron/service" - "log" - "strings" -) - -func main() { - grpcAddress := flag.String("grpcAddress", "", - "gRPC address: example: -grpcAddress localhost:50051") - - hash := flag.String("hash", - "", - "hash: ") - - flag.Parse() - - if (strings.EqualFold("", *hash) && len(*hash) == 0) || (strings.EqualFold("", *grpcAddress) && len(*grpcAddress) == 0) { - log.Fatalln("./get-transaction-by-id -grpcAddress localhost" + - ":50051 -hash ") - } - - client := service.NewGrpcClient(*grpcAddress) - client.Start() - defer client.Conn.Close() - - transaction := client.GetTransactionById(*hash) - - fmt.Printf("transaction: %v\n", transaction) -} diff --git a/program/listnodes.go b/program/listnodes.go deleted file mode 100644 index d100df08..00000000 --- a/program/listnodes.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "github.com/fbsobreira/gotron/service" - "log" - "strings" -) - -func main() { - grpcAddress := flag.String("grpcAddress", "", - "gRPC address: example: -grpcAddress localhost:50051") - - flag.Parse() - - if strings.EqualFold("", *grpcAddress) && len(*grpcAddress) == 0 { - log.Fatalln("./list-nodes -grpcAddress localhost:50051") - } - - client := service.NewGrpcClient(*grpcAddress) - client.Start() - defer client.Conn.Close() - - nodes := client.ListNodes() - - for i, v := range nodes.Nodes { - host := string(v.Address.Host) - port := v.Address.Port - fmt.Printf("index: %d, node: host: %v, port: %d\n", i, host, port) - } -} diff --git a/program/listwitnesses.go b/program/listwitnesses.go deleted file mode 100644 index 48f05d47..00000000 --- a/program/listwitnesses.go +++ /dev/null @@ -1,42 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "github.com/fbsobreira/gotron/common/hexutil" - "github.com/fbsobreira/gotron/service" - "log" - "strings" -) - -func main() { - grpcAddress := flag.String("grpcAddress", "", - "gRPC address: example: -grpcAddress localhost:50051") - - flag.Parse() - - if strings.EqualFold("", *grpcAddress) && len(*grpcAddress) == 0 { - log.Fatalln("./list-witnesses -grpcAddress localhost:50051") - } - - client := service.NewGrpcClient(*grpcAddress) - client.Start() - defer client.Conn.Close() - - witnesses := client.ListWitnesses() - - for i, v := range witnesses.Witnesses { - addr := hexutil.Encode(v.Address) - u := v.Url - totalProduced := v.TotalProduced - totalMissed := v.TotalMissed - latestBlockNum := v.LatestBlockNum - latestSlotNum := v.LatestSlotNum - isJobs := v.IsJobs - fmt.Printf("index: %d, witness: address: %s, url: %s, "+ - "total produced: %d, total missed: %d, latest block num: %d, "+ - "latest slot num: %d, is jobs: %v\n", i, - addr, u, - totalProduced, totalMissed, latestBlockNum, latestSlotNum, isJobs) - } -} diff --git a/program/totaltransaction.go b/program/totaltransaction.go deleted file mode 100644 index 574401b5..00000000 --- a/program/totaltransaction.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "github.com/fbsobreira/gotron/service" - "log" - "strings" -) - -func main() { - grpcAddress := flag.String("grpcAddress", "", - "gRPC address: example: -grpcAddress localhost:50051") - - flag.Parse() - - if strings.EqualFold("", *grpcAddress) && len(*grpcAddress) == 0 { - log.Fatalln("./total-transaction -grpcAddress localhost:50051") - } - - client := service.NewGrpcClient(*grpcAddress) - client.Start() - defer client.Conn.Close() - - num := client.TotalTransaction() - - fmt.Printf("total transaction: %v\n", num) -} diff --git a/program/transfer.go b/program/transfer.go deleted file mode 100644 index dcb59d50..00000000 --- a/program/transfer.go +++ /dev/null @@ -1,48 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "github.com/fbsobreira/gotron/common/crypto" - "github.com/fbsobreira/gotron/service" - "log" - "strings" -) - -func main() { - grpcAddress := flag.String("grpcAddress", "", - "gRPC address: example: -grpcAddress localhost:50051") - - ownerPrivateKey := flag.String("ownerPrivateKey", "", - "ownerPrivateKey: ") - - address := flag.String("toAddress", "", - "toAddress: ") - - amount := flag.Int64("amount", 0, - "amount: ") - - flag.Parse() - - if (*amount <= 0) || (strings.EqualFold("", *ownerPrivateKey) && len( - *ownerPrivateKey) == 0) || (strings. - EqualFold("", *address) && len(*address) == 0) || (strings.EqualFold("", *grpcAddress) && len(*grpcAddress) == 0) { - log.Fatalln("./transfer -grpcAddress localhost" + - ":50051 -ownerPrivateKey -toAddress" + - " -amount ") - } - - client := service.NewGrpcClient(*grpcAddress) - client.Start() - defer client.Conn.Close() - - key, err := crypto.GetPrivateKeyByHexString(*ownerPrivateKey) - - if err != nil { - log.Fatalf("get private key by hex string error: %v", err) - } - - result := client.Transfer(key, *address, *amount) - - fmt.Printf("result: %v\n", result) -} diff --git a/program/transferAsset.go b/program/transferAsset.go deleted file mode 100644 index ec0f30bd..00000000 --- a/program/transferAsset.go +++ /dev/null @@ -1,52 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "log" - "strings" - - "github.com/fbsobreira/gotron/common/crypto" - "github.com/fbsobreira/gotron/service" -) - -func main() { - grpcAddress := flag.String("grpcAddress", "", - "gRPC address: example: -grpcAddress localhost:50051") - - ownerPrivateKey := flag.String("ownerPrivateKey", "", - "ownerPrivateKey: ") - - address := flag.String("toAddress", "", - "toAddress: ") - - assetID := flag.String("assetID", "", - "assetID: ") - - amount := flag.Int64("amount", 0, - "amount: ") - - flag.Parse() - - if (*amount <= 0) || len(*assetID) != 7 || (strings.EqualFold("", *ownerPrivateKey) && len( - *ownerPrivateKey) == 0) || (strings. - EqualFold("", *address) && len(*address) == 0) || (strings.EqualFold("", *grpcAddress) && len(*grpcAddress) == 0) { - log.Fatalln("./transfer -grpcAddress localhost" + - ":50051 -ownerPrivateKey -toAddress" + - " -amount -assetID ") - } - - client := service.NewGrpcClient(*grpcAddress) - client.Start() - defer client.Conn.Close() - - key, err := crypto.GetPrivateKeyByHexString(*ownerPrivateKey) - - if err != nil { - log.Fatalf("get private key by hex string error: %v", err) - } - - result := client.TransferAsset(key, *address, *assetID, *amount) - - fmt.Printf("result: %v\n", result) -} diff --git a/program/unfreezebalance.go b/program/unfreezebalance.go deleted file mode 100644 index 3c45c5a6..00000000 --- a/program/unfreezebalance.go +++ /dev/null @@ -1,41 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "github.com/fbsobreira/gotron/common/crypto" - "github.com/fbsobreira/gotron/service" - "log" - "strings" -) - -func main() { - grpcAddress := flag.String("grpcAddress", "39.106.178.126:50051", - "gRPC address: example: -grpcAddress localhost:50051") - - ownerPrivateKey := flag.String("ownerPrivateKey", "cbe57d98134c118ed0d219c0c8bc4154372c02c1e13b5cce30dd22ecd7bed19e", - "ownerPrivateKey: ") - - flag.Parse() - - if (strings.EqualFold("", *ownerPrivateKey) && len( - *ownerPrivateKey) == 0) || (strings.EqualFold( - "", *grpcAddress) && len(*grpcAddress) == 0) { - log.Fatalln("./unfreeze-balance -grpcAddress localhost" + - ":50051 -ownerPrivateKey ") - } - - client := service.NewGrpcClient(*grpcAddress) - client.Start() - defer client.Conn.Close() - - key, err := crypto.GetPrivateKeyByHexString(*ownerPrivateKey) - - if err != nil { - log.Fatalf("get private key by hex string error: %v", err) - } - - result := client.UnfreezeBalance(key) - - fmt.Printf("result: %v\n", result) -} diff --git a/program/updateaccount.go b/program/updateaccount.go deleted file mode 100644 index 840801b4..00000000 --- a/program/updateaccount.go +++ /dev/null @@ -1,44 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "github.com/fbsobreira/gotron/common/crypto" - "github.com/fbsobreira/gotron/service" - "log" - "strings" -) - -func main() { - grpcAddress := flag.String("grpcAddress", "", - "gRPC address: example: -grpcAddress localhost:50051") - - ownerPrivateKey := flag.String("ownerPrivateKey", "", - "ownerPrivateKey: ") - - name := flag.String("newAccountName", "", - "newAccountName: ") - - flag.Parse() - - if (strings.EqualFold("", *ownerPrivateKey) && len(*ownerPrivateKey) == 0) || (strings. - EqualFold("", *name) && len(*name) == 0) || (strings.EqualFold("", *grpcAddress) && len(*grpcAddress) == 0) { - log.Fatalln("./update-account -grpcAddress localhost" + - ":50051 -ownerPrivateKey -newAccountName" + - " ") - } - - client := service.NewGrpcClient(*grpcAddress) - client.Start() - defer client.Conn.Close() - - key, err := crypto.GetPrivateKeyByHexString(*ownerPrivateKey) - - if err != nil { - log.Fatalf("get private key by hex string error: %v", err) - } - - result := client.UpdateAccount(key, *name) - - fmt.Printf("result: %v\n", result) -} diff --git a/program/updateassetissue.go b/program/updateassetissue.go deleted file mode 100644 index 7b752dd2..00000000 --- a/program/updateassetissue.go +++ /dev/null @@ -1,68 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "github.com/fbsobreira/gotron/common/crypto" - "github.com/fbsobreira/gotron/service" - "log" - "strings" -) - -func main() { - grpcAddress := flag.String("grpcAddress", "", - "gRPC address: example: -grpcAddress localhost:50051") - - ownerPrivateKey := flag.String("ownerPrivateKey", "", - "ownerPrivateKey: ") - - description := flag.String("description", "", - "description: ") - - urlStr := flag.String("url", "", - "url: ") - - newLimit := flag.Int64("newLimit", 0, - "newLimit: ") - - newPublicLimit := flag.Int64("newPublicLimit", 0, - "newPublicLimit: ") - - flag.Parse() - - if (strings.EqualFold("", *ownerPrivateKey) && len(*ownerPrivateKey) == 0) || - (strings.EqualFold("", *grpcAddress) && len(*grpcAddress) == 0) || - (strings.EqualFold("", *description) && len(*description) == 0) || - (strings.EqualFold("", *urlStr) && len(*urlStr) == 0) || - (*newLimit < 0) || - (*newPublicLimit < 0) { - log.Fatalln("./update-asset-issue " + - "-grpcAddress localhost:50051 " + - "-ownerPrivateKey " + - "-description " + - "-url " + - "-newLimit " + - "-newPublicLimit ") - } - - client := service.NewGrpcClient(*grpcAddress) - client.Start() - defer client.Conn.Close() - - key, err := crypto.GetPrivateKeyByHexString(*ownerPrivateKey) - - if err != nil { - log.Fatalf("get private key by hex string error: %v", err) - } - - result := client.UpdateAssetIssue( - key, - *description, - *urlStr, - *newLimit, - *newPublicLimit) - - fmt.Printf("result: %v\n", result) -} diff --git a/protocol_util/completeTx.pb.go b/protocol_util/completeTx.pb.go deleted file mode 100644 index 32653f19..00000000 --- a/protocol_util/completeTx.pb.go +++ /dev/null @@ -1,110 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: completeTx.proto - -package protocol_util - -import ( - fmt "fmt" - api "github.com/fbsobreira/gotron/api" - core "github.com/fbsobreira/gotron/core" - proto "github.com/golang/protobuf/proto" - math "math" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -type ProtoCompleteTransaction struct { - BlockNumber uint64 `protobuf:"varint,1,opt,name=BlockNumber,proto3" json:"BlockNumber,omitempty"` - BlockTime uint64 `protobuf:"varint,2,opt,name=BlockTime,proto3" json:"BlockTime,omitempty"` - Tx *api.TransactionExtention `protobuf:"bytes,3,opt,name=Tx,proto3" json:"Tx,omitempty"` - Info *core.TransactionInfo `protobuf:"bytes,4,opt,name=Info,proto3" json:"Info,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ProtoCompleteTransaction) Reset() { *m = ProtoCompleteTransaction{} } -func (m *ProtoCompleteTransaction) String() string { return proto.CompactTextString(m) } -func (*ProtoCompleteTransaction) ProtoMessage() {} -func (*ProtoCompleteTransaction) Descriptor() ([]byte, []int) { - return fileDescriptor_0775e206b470b626, []int{0} -} - -func (m *ProtoCompleteTransaction) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ProtoCompleteTransaction.Unmarshal(m, b) -} -func (m *ProtoCompleteTransaction) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ProtoCompleteTransaction.Marshal(b, m, deterministic) -} -func (m *ProtoCompleteTransaction) XXX_Merge(src proto.Message) { - xxx_messageInfo_ProtoCompleteTransaction.Merge(m, src) -} -func (m *ProtoCompleteTransaction) XXX_Size() int { - return xxx_messageInfo_ProtoCompleteTransaction.Size(m) -} -func (m *ProtoCompleteTransaction) XXX_DiscardUnknown() { - xxx_messageInfo_ProtoCompleteTransaction.DiscardUnknown(m) -} - -var xxx_messageInfo_ProtoCompleteTransaction proto.InternalMessageInfo - -func (m *ProtoCompleteTransaction) GetBlockNumber() uint64 { - if m != nil { - return m.BlockNumber - } - return 0 -} - -func (m *ProtoCompleteTransaction) GetBlockTime() uint64 { - if m != nil { - return m.BlockTime - } - return 0 -} - -func (m *ProtoCompleteTransaction) GetTx() *api.TransactionExtention { - if m != nil { - return m.Tx - } - return nil -} - -func (m *ProtoCompleteTransaction) GetInfo() *core.TransactionInfo { - if m != nil { - return m.Info - } - return nil -} - -func init() { - proto.RegisterType((*ProtoCompleteTransaction)(nil), "protocol.ProtoCompleteTransaction") -} - -func init() { proto.RegisterFile("completeTx.proto", fileDescriptor_0775e206b470b626) } - -var fileDescriptor_0775e206b470b626 = []byte{ - // 224 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x4e, 0xcd, 0x4a, 0x03, 0x31, - 0x10, 0x66, 0xd7, 0x45, 0x34, 0x45, 0x94, 0x9c, 0x62, 0x11, 0x59, 0x3c, 0x15, 0xd1, 0x04, 0xf4, - 0x0d, 0x2a, 0x1e, 0xbc, 0x88, 0x2c, 0x39, 0x79, 0x91, 0x24, 0xa4, 0x35, 0x98, 0x64, 0xc2, 0x34, - 0x0b, 0x79, 0x35, 0xdf, 0x4e, 0x36, 0x65, 0xb1, 0x87, 0x9e, 0xe6, 0xfb, 0x9d, 0x19, 0x72, 0x65, - 0x20, 0x24, 0x6f, 0xb3, 0x95, 0x85, 0x27, 0x84, 0x0c, 0xf4, 0xac, 0x0e, 0x03, 0x7e, 0x79, 0x69, - 0x00, 0xad, 0x90, 0x08, 0x71, 0x6f, 0x2d, 0x2f, 0x54, 0x72, 0x42, 0x25, 0xb7, 0xa7, 0x77, 0xbf, - 0x0d, 0x61, 0x1f, 0x13, 0x7a, 0x99, 0x77, 0xa0, 0x8a, 0x3b, 0x65, 0xb2, 0x83, 0x48, 0x7b, 0xb2, - 0x58, 0x7b, 0x30, 0x3f, 0xef, 0x63, 0xd0, 0x16, 0x59, 0xd3, 0x37, 0xab, 0x6e, 0x38, 0x94, 0xe8, - 0x0d, 0x39, 0xaf, 0x54, 0xba, 0x60, 0x59, 0x5b, 0xfd, 0x7f, 0x81, 0x72, 0xd2, 0xca, 0xc2, 0x4e, - 0xfa, 0x66, 0xb5, 0x78, 0xba, 0xe5, 0xf3, 0x4f, 0xfc, 0xe0, 0xc4, 0x6b, 0xc9, 0x36, 0x4e, 0x60, - 0x68, 0x65, 0xa1, 0x8f, 0xa4, 0x7b, 0x8b, 0x1b, 0x60, 0x5d, 0x6d, 0x5c, 0x1f, 0x6d, 0x4c, 0x81, - 0xa1, 0xc6, 0xd6, 0x0f, 0x9f, 0xf7, 0x5b, 0x97, 0xbf, 0x47, 0xcd, 0x0d, 0x04, 0xb1, 0xd1, 0x3b, - 0xd0, 0x68, 0x1d, 0x2a, 0xb1, 0x85, 0x8c, 0x10, 0xc5, 0x5c, 0xff, 0x1a, 0xb3, 0xf3, 0xfa, 0xb4, - 0xd2, 0xe7, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xaa, 0x2b, 0xa2, 0x1a, 0x2e, 0x01, 0x00, 0x00, -} diff --git a/protocol_util/completeTx.proto b/protocol_util/completeTx.proto deleted file mode 100644 index fcd271ca..00000000 --- a/protocol_util/completeTx.proto +++ /dev/null @@ -1,15 +0,0 @@ -syntax = "proto3"; - -import "core/Tron.proto"; -import "api/api.proto"; - -package protocol; - -option go_package = "github.com/fbsobreira/gotron/protocol_util"; - -message ProtoCompleteTransaction { - uint64 BlockNumber = 1; - uint64 BlockTime = 2; - TransactionExtention Tx = 3; - TransactionInfo Info = 4; -} \ No newline at end of file diff --git a/routers/commentsRouter___________________Users_xiexiaodong_go_src_github_com_sasaxie_go-client-api_controllers.go b/routers/commentsRouter___________________Users_xiexiaodong_go_src_github_com_sasaxie_go-client-api_controllers.go deleted file mode 100644 index 42e345d6..00000000 --- a/routers/commentsRouter___________________Users_xiexiaodong_go_src_github_com_sasaxie_go-client-api_controllers.go +++ /dev/null @@ -1,234 +0,0 @@ -package routers - -import ( - "github.com/astaxie/beego" - "github.com/astaxie/beego/context/param" -) - -func init() { - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:AccountController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:AccountController"], - beego.ControllerComments{ - Method: "Get", - Router: `/address/:address`, - AllowHTTPMethods: []string{"get"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:AccountController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:AccountController"], - beego.ControllerComments{ - Method: "CreateAccount", - Router: `/create`, - AllowHTTPMethods: []string{"post"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:AccountController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:AccountController"], - beego.ControllerComments{ - Method: "NetMessage", - Router: `/net-message/address/:address`, - AllowHTTPMethods: []string{"get"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:AssetIssueController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:AssetIssueController"], - beego.ControllerComments{ - Method: "Address", - Router: `/address/:address`, - AllowHTTPMethods: []string{"get"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:AssetIssueController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:AssetIssueController"], - beego.ControllerComments{ - Method: "List", - Router: `/list`, - AllowHTTPMethods: []string{"get"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:AssetIssueController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:AssetIssueController"], - beego.ControllerComments{ - Method: "Name", - Router: `/name/:name`, - AllowHTTPMethods: []string{"get"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:BlockController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:BlockController"], - beego.ControllerComments{ - Method: "Id", - Router: `/id/:id`, - AllowHTTPMethods: []string{"get"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:BlockController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:BlockController"], - beego.ControllerComments{ - Method: "LatestNum", - Router: `/latest-num/:num`, - AllowHTTPMethods: []string{"get"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:BlockController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:BlockController"], - beego.ControllerComments{ - Method: "Now", - Router: `/now`, - AllowHTTPMethods: []string{"get"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:BlockController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:BlockController"], - beego.ControllerComments{ - Method: "Num", - Router: `/num/:num`, - AllowHTTPMethods: []string{"get"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:BlockController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:BlockController"], - beego.ControllerComments{ - Method: "GetBlockByLimit", - Router: `/start/:start/end/:end`, - AllowHTTPMethods: []string{"get"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:NodeController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:NodeController"], - beego.ControllerComments{ - Method: "List", - Router: `/list`, - AllowHTTPMethods: []string{"get"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:NumberMessageController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:NumberMessageController"], - beego.ControllerComments{ - Method: "NextMaintenanceTime", - Router: `/next-maintenance-time`, - AllowHTTPMethods: []string{"get"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:NumberMessageController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:NumberMessageController"], - beego.ControllerComments{ - Method: "TotalTransaction", - Router: `/total-transaction`, - AllowHTTPMethods: []string{"get"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:ObjectController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:ObjectController"], - beego.ControllerComments{ - Method: "Post", - Router: `/`, - AllowHTTPMethods: []string{"post"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:ObjectController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:ObjectController"], - beego.ControllerComments{ - Method: "GetAll", - Router: `/`, - AllowHTTPMethods: []string{"get"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:ObjectController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:ObjectController"], - beego.ControllerComments{ - Method: "Get", - Router: `/:objectId`, - AllowHTTPMethods: []string{"get"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:ObjectController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:ObjectController"], - beego.ControllerComments{ - Method: "Put", - Router: `/:objectId`, - AllowHTTPMethods: []string{"put"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:ObjectController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:ObjectController"], - beego.ControllerComments{ - Method: "Delete", - Router: `/:objectId`, - AllowHTTPMethods: []string{"delete"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:TransactionController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:TransactionController"], - beego.ControllerComments{ - Method: "Id", - Router: `/id/:id`, - AllowHTTPMethods: []string{"get"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:UserController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:UserController"], - beego.ControllerComments{ - Method: "Post", - Router: `/`, - AllowHTTPMethods: []string{"post"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:UserController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:UserController"], - beego.ControllerComments{ - Method: "GetAll", - Router: `/`, - AllowHTTPMethods: []string{"get"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:UserController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:UserController"], - beego.ControllerComments{ - Method: "Get", - Router: `/:uid`, - AllowHTTPMethods: []string{"get"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:UserController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:UserController"], - beego.ControllerComments{ - Method: "Put", - Router: `/:uid`, - AllowHTTPMethods: []string{"put"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:UserController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:UserController"], - beego.ControllerComments{ - Method: "Delete", - Router: `/:uid`, - AllowHTTPMethods: []string{"delete"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:UserController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:UserController"], - beego.ControllerComments{ - Method: "Login", - Router: `/login`, - AllowHTTPMethods: []string{"get"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:UserController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:UserController"], - beego.ControllerComments{ - Method: "Logout", - Router: `/logout`, - AllowHTTPMethods: []string{"get"}, - MethodParams: param.Make(), - Params: nil}) - - beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:WitnessController"] = append(beego.GlobalControllerRouter["github.com/fbsobreira/gotron/controllers:WitnessController"], - beego.ControllerComments{ - Method: "List", - Router: `/list`, - AllowHTTPMethods: []string{"get"}, - MethodParams: param.Make(), - Params: nil}) - -} diff --git a/routers/router.go b/routers/router.go deleted file mode 100644 index 89f3c3e2..00000000 --- a/routers/router.go +++ /dev/null @@ -1,51 +0,0 @@ -// @APIVersion 1.0.0 -// @Title gotron Test API -// @Description gotron is tron-java grpc client -// @TermsOfServiceUrl https://tron.network/ -package routers - -import ( - "github.com/astaxie/beego" - "github.com/fbsobreira/gotron/controllers" -) - -func init() { - ns := beego.NewNamespace("/v1", - beego.NSNamespace("/account", - beego.NSInclude( - &controllers.AccountController{}, - ), - ), - beego.NSNamespace("/witness", - beego.NSInclude( - &controllers.WitnessController{}, - ), - ), - beego.NSNamespace("/node", - beego.NSInclude( - &controllers.NodeController{}, - ), - ), - beego.NSNamespace("/block", - beego.NSInclude( - &controllers.BlockController{}, - ), - ), - beego.NSNamespace("/asset-issue", - beego.NSInclude( - &controllers.AssetIssueController{}, - ), - ), - beego.NSNamespace("/number", - beego.NSInclude( - &controllers.NumberMessageController{}, - ), - ), - beego.NSNamespace("/transaction", - beego.NSInclude( - &controllers.TransactionController{}, - ), - ), - ) - beego.AddNamespace(ns) -} diff --git a/service/client.go b/service/client.go deleted file mode 100644 index c1445798..00000000 --- a/service/client.go +++ /dev/null @@ -1,863 +0,0 @@ -package service - -import ( - "context" - "crypto/ecdsa" - "log" - "strconv" - "time" - - "github.com/fbsobreira/gotron/api" - "github.com/fbsobreira/gotron/common/base58" - "github.com/fbsobreira/gotron/common/crypto" - "github.com/fbsobreira/gotron/common/hexutil" - "github.com/fbsobreira/gotron/core" - "github.com/fbsobreira/gotron/util" - "google.golang.org/grpc" -) - -const GrpcTimeout = 5 * time.Second - -type GrpcClient struct { - Address string - Conn *grpc.ClientConn - Client api.WalletClient -} - -func NewGrpcClient(address string) *GrpcClient { - client := new(GrpcClient) - client.Address = address - return client -} - -func (g *GrpcClient) Start() { - var err error - g.Conn, err = grpc.Dial(g.Address, grpc.WithInsecure()) - if err != nil { - log.Fatalf("did not connect: %v\n", err) - } - - g.Client = api.NewWalletClient(g.Conn) -} - -func (g *GrpcClient) ListWitnesses() *api.WitnessList { - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - witnessList, err := g.Client.ListWitnesses(ctx, new(api.EmptyMessage)) - - if err != nil { - log.Fatalf("get witnesses error: %v\n", err) - } - - return witnessList -} - -func (g *GrpcClient) ListNodes() *api.NodeList { - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - nodeList, err := g.Client.ListNodes(ctx, new(api.EmptyMessage)) - - if err != nil { - log.Fatalf("get nodes error: %v\n", err) - } - - return nodeList -} - -func (g *GrpcClient) GetAccount(address string) *core.Account { - account := new(core.Account) - - account.Address = base58.DecodeCheck(address) - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - result, err := g.Client.GetAccount(ctx, account) - - if err != nil { - log.Fatalf("get account error: %v\n", err) - } - - return result -} - -func (g *GrpcClient) GetNowBlock() *core.Block { - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - result, err := g.Client.GetNowBlock(ctx, new(api.EmptyMessage)) - - if err != nil { - log.Fatalf("get now block error: %v\n", err) - } - - return result -} - -func (g *GrpcClient) GetAssetIssueByAccount(address string) *api.AssetIssueList { - account := new(core.Account) - - account.Address = base58.DecodeCheck(address) - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - result, err := g.Client.GetAssetIssueByAccount(ctx, account) - - if err != nil { - log.Fatalf("get asset issue by account error: %v", err) - } - - return result -} - -func (g *GrpcClient) GetNextMaintenanceTime() *api.NumberMessage { - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - result, err := g.Client.GetNextMaintenanceTime(ctx, - new(api.EmptyMessage)) - - if err != nil { - log.Fatalf("get next maintenance time error: %v", err) - } - - return result -} - -func (g *GrpcClient) TotalTransaction() *api.NumberMessage { - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - result, err := g.Client.TotalTransaction(ctx, - new(api.EmptyMessage)) - - if err != nil { - log.Fatalf("total transaction error: %v", err) - } - - return result -} - -func (g *GrpcClient) GetAccountNet(address string) *api.AccountNetMessage { - account := new(core.Account) - - account.Address = base58.DecodeCheck(address) - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - result, err := g.Client.GetAccountNet(ctx, account) - - if err != nil { - log.Fatalf("get account net error: %v", err) - } - - return result -} - -func (g *GrpcClient) GetAssetIssueByName(name string) *core.AssetIssueContract { - - assetName := new(api.BytesMessage) - assetName.Value = []byte(name) - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - result, err := g.Client.GetAssetIssueByName(ctx, assetName) - - if err != nil { - log.Fatalf("get asset issue by name error: %v", err) - } - - return result -} - -func (g *GrpcClient) GetBlockByNum(num int64) *core.Block { - numMessage := new(api.NumberMessage) - numMessage.Num = num - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - result, err := g.Client.GetBlockByNum(ctx, numMessage) - - if err != nil { - log.Fatalf("get block by num error: %v", err) - } - - return result -} - -func (g *GrpcClient) GetBlockById(id string) *core.Block { - blockId := new(api.BytesMessage) - var err error - - blockId.Value, err = hexutil.Decode(id) - - if err != nil { - log.Fatalf("get block by id error: %v", err) - } - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - result, err := g.Client.GetBlockById(ctx, blockId) - - if err != nil { - log.Fatalf("get block by id error: %v", err) - } - - return result -} - -func (g *GrpcClient) GetAssetIssueList() *api.AssetIssueList { - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - result, err := g.Client.GetAssetIssueList(ctx, new(api.EmptyMessage)) - - if err != nil { - log.Fatalf("get asset issue list error: %v", err) - } - - return result -} - -func (g *GrpcClient) GetBlockByLimitNext(start, end int64) *api.BlockList { - blockLimit := new(api.BlockLimit) - blockLimit.StartNum = start - blockLimit.EndNum = end - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - result, err := g.Client.GetBlockByLimitNext(ctx, blockLimit) - - if err != nil { - log.Fatalf("get block by limit next error: %v", err) - } - - return result -} - -func (g *GrpcClient) GetTransactionById(id string) *core.Transaction { - transactionId := new(api.BytesMessage) - var err error - - transactionId.Value, err = hexutil.Decode(id) - - if err != nil { - log.Fatalf("get transaction by id error: %v", err) - } - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - result, err := g.Client.GetTransactionById(ctx, transactionId) - - if err != nil { - log.Fatalf("get transaction by limit next error: %v", err) - } - - return result -} - -func (g *GrpcClient) GetBlockByLatestNum(num int64) *api.BlockList { - numMessage := new(api.NumberMessage) - numMessage.Num = num - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - result, err := g.Client.GetBlockByLatestNum(ctx, numMessage) - - if err != nil { - log.Fatalf("get block by latest num error: %v", err) - } - - return result -} - -func (g *GrpcClient) CreateAccount(ownerKey *ecdsa.PrivateKey, - accountAddress string) *api.Return { - - accountCreateContract := new(core.AccountCreateContract) - accountCreateContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey.PublicKey).Bytes() - accountCreateContract.AccountAddress = base58.DecodeCheck(accountAddress) - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - accountCreateTransaction, err := g.Client.CreateAccount(ctx, - accountCreateContract) - - if err != nil { - log.Fatalf("create account error: %v", err) - } - - if accountCreateTransaction == nil || len(accountCreateTransaction. - GetRawData().GetContract()) == 0 { - log.Fatalf("create account error: invalid transaction") - } - - util.SignTransaction(accountCreateTransaction, ownerKey) - - result, err := g.Client.BroadcastTransaction(ctx, - accountCreateTransaction) - - if err != nil { - log.Fatalf("create account error: %v", err) - } - - return result -} - -func (g *GrpcClient) CreateAccountByContract(accountCreateContract *core. - AccountCreateContract) (*core.Transaction, error) { - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - return g.Client.CreateAccount(ctx, accountCreateContract) -} - -func (g *GrpcClient) UpdateAccount(ownerKey *ecdsa.PrivateKey, - accountName string) *api.Return { - - var err error - accountUpdateContract := new(core.AccountUpdateContract) - accountUpdateContract.AccountName = []byte(accountName) - accountUpdateContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey. - PublicKey).Bytes() - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - accountUpdateTransaction, err := g.Client.UpdateAccount(ctx, - accountUpdateContract) - - if err != nil { - log.Fatalf("update account error: %v", err) - } - - if accountUpdateTransaction == nil || len(accountUpdateTransaction. - GetRawData().GetContract()) == 0 { - log.Fatalf("update account error: invalid transaction") - } - - util.SignTransaction(accountUpdateTransaction, ownerKey) - - result, err := g.Client.BroadcastTransaction(ctx, - accountUpdateTransaction) - - if err != nil { - log.Fatalf("update account error: %v", err) - } - - return result -} - -func (g *GrpcClient) Transfer(ownerKey *ecdsa.PrivateKey, toAddress string, - amount int64) *api.Return { - - transferContract := new(core.TransferContract) - transferContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey. - PublicKey).Bytes() - transferContract.ToAddress = base58.DecodeCheck(toAddress) - transferContract.Amount = amount - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - transferTransaction, err := g.Client.CreateTransaction(ctx, - transferContract) - - if err != nil { - log.Fatalf("transfer error: %v", err) - } - - if transferTransaction == nil || len(transferTransaction. - GetRawData().GetContract()) == 0 { - log.Fatalf("transfer error: invalid transaction") - } - - util.SignTransaction(transferTransaction, ownerKey) - - result, err := g.Client.BroadcastTransaction(ctx, - transferTransaction) - - if err != nil { - log.Fatalf("transfer error: %v", err) - } - - return result -} - -func (g *GrpcClient) FreezeBalance(ownerKey *ecdsa.PrivateKey, - frozenBalance, frozenDuration int64) *api.Return { - freezeBalanceContract := new(core.FreezeBalanceContract) - freezeBalanceContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey. - PublicKey).Bytes() - freezeBalanceContract.FrozenBalance = frozenBalance - freezeBalanceContract.FrozenDuration = frozenDuration - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - freezeBalanceTransaction, err := g.Client.FreezeBalance(ctx, - freezeBalanceContract) - - if err != nil { - log.Fatalf("freeze balance error: %v", err) - } - - if freezeBalanceTransaction == nil || len(freezeBalanceTransaction. - GetRawData().GetContract()) == 0 { - log.Fatalf("freeze balance error: invalid transaction") - } - - util.SignTransaction(freezeBalanceTransaction, ownerKey) - - result, err := g.Client.BroadcastTransaction(ctx, - freezeBalanceTransaction) - - if err != nil { - log.Fatalf("freeze balance error: %v", err) - } - - return result -} - -func (g *GrpcClient) UnfreezeBalance(ownerKey *ecdsa.PrivateKey) *api.Return { - unfreezeBalanceContract := new(core.UnfreezeBalanceContract) - unfreezeBalanceContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey.PublicKey).Bytes() - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - unfreezeBalanceTransaction, err := g.Client.UnfreezeBalance(ctx, - unfreezeBalanceContract) - - if err != nil { - log.Fatalf("unfreeze balance error: %v", err) - } - - if unfreezeBalanceTransaction == nil || len(unfreezeBalanceTransaction. - GetRawData().GetContract()) == 0 { - log.Fatalf("unfreeze balance error: invalid transaction") - } - - util.SignTransaction(unfreezeBalanceTransaction, ownerKey) - - result, err := g.Client.BroadcastTransaction(ctx, - unfreezeBalanceTransaction) - - if err != nil { - log.Fatalf("unfreeze balance error: %v", err) - } - - return result -} - -func (g *GrpcClient) CreateAssetIssue(ownerKey *ecdsa.PrivateKey, - name, description, abbr, urlStr string, totalSupply, startTime, endTime, - FreeAssetNetLimit, - PublicFreeAssetNetLimit int64, trxNum, - icoNum, voteScore int32, frozenSupply map[string]string) *api.Return { - assetIssueContract := new(core.AssetIssueContract) - - assetIssueContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey. - PublicKey).Bytes() - - assetIssueContract.Name = []byte(name) - - assetIssueContract.Abbr = []byte(abbr) - - if totalSupply <= 0 { - log.Fatalf("create asset issue error: total supply <= 0") - } - assetIssueContract.TotalSupply = totalSupply - - if trxNum <= 0 { - log.Fatalf("create asset issue error: trxNum <= 0") - } - assetIssueContract.TrxNum = trxNum - - if icoNum <= 0 { - log.Fatalf("create asset issue error: num <= 0") - } - assetIssueContract.Num = icoNum - - now := time.Now().UnixNano() / 1000000 - if startTime <= now { - log.Fatalf("create asset issue error: start time <= current time") - } - assetIssueContract.StartTime = startTime - - if endTime <= startTime { - log.Fatalf("create asset issue error: end time <= start time") - } - assetIssueContract.EndTime = endTime - - if FreeAssetNetLimit < 0 { - log.Fatalf("create asset issue error: free asset net limit < 0") - } - assetIssueContract.FreeAssetNetLimit = FreeAssetNetLimit - - if PublicFreeAssetNetLimit < 0 { - log.Fatalf("create asset issue error: public free asset net limit < 0") - } - assetIssueContract.PublicFreeAssetNetLimit = PublicFreeAssetNetLimit - - assetIssueContract.VoteScore = voteScore - assetIssueContract.Description = []byte(description) - assetIssueContract.Url = []byte(urlStr) - - for key, value := range frozenSupply { - amount, err := strconv.ParseInt(value, 10, 64) - if err != nil { - log.Fatalf("create asset issue error: convert error: %v", err) - } - days, err := strconv.ParseInt(key, 10, 64) - if err != nil { - log.Fatalf("create asset issue error: convert error: %v", err) - } - assetIssueContractFrozenSupply := new(core. - AssetIssueContract_FrozenSupply) - assetIssueContractFrozenSupply.FrozenAmount = amount - assetIssueContractFrozenSupply.FrozenDays = days - assetIssueContract.FrozenSupply = append(assetIssueContract. - FrozenSupply, assetIssueContractFrozenSupply) - } - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - assetIssueTransaction, err := g.Client.CreateAssetIssue(ctx, - assetIssueContract) - - if err != nil { - log.Fatalf("create asset issue error: %v", err) - } - - if assetIssueTransaction == nil || len(assetIssueTransaction. - GetRawData().GetContract()) == 0 { - log.Fatalf("create asset issue error: invalid transaction") - } - - util.SignTransaction(assetIssueTransaction, ownerKey) - - result, err := g.Client.BroadcastTransaction(ctx, - assetIssueTransaction) - - if err != nil { - log.Fatalf("create asset issue error: %v", err) - } - - return result -} - -func (g *GrpcClient) UpdateAssetIssue(ownerKey *ecdsa.PrivateKey, - description, urlStr string, - newLimit, newPublicLimit int64) *api.Return { - - updateAssetContract := new(core.UpdateAssetContract) - - updateAssetContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey. - PublicKey).Bytes() - - updateAssetContract.Description = []byte(description) - updateAssetContract.Url = []byte(urlStr) - updateAssetContract.NewLimit = newLimit - updateAssetContract.NewPublicLimit = newPublicLimit - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - updateAssetTransaction, err := g.Client.UpdateAsset(ctx, - updateAssetContract) - - if err != nil { - log.Fatalf("update asset issue error: %v", err) - } - - if updateAssetTransaction == nil || len(updateAssetTransaction. - GetRawData().GetContract()) == 0 { - log.Fatalf("update asset issue error: invalid transaction") - } - - util.SignTransaction(updateAssetTransaction, ownerKey) - - result, err := g.Client.BroadcastTransaction(ctx, - updateAssetTransaction) - - if err != nil { - log.Fatalf("update asset issue error: %v", err) - } - - return result -} - -func (g *GrpcClient) TransferAsset(ownerKey *ecdsa.PrivateKey, toAddress, - assetName string, amount int64) *api.Return { - - transferAssetContract := new(core.TransferAssetContract) - transferAssetContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey. - PublicKey).Bytes() - transferAssetContract.ToAddress = base58.DecodeCheck(toAddress) - transferAssetContract.AssetName = []byte(assetName) - transferAssetContract.Amount = amount - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - transferAssetTransaction, err := g.Client.TransferAsset(ctx, - transferAssetContract) - - if err != nil { - log.Fatalf("transfer asset error: %v", err) - } - - if transferAssetTransaction == nil || len(transferAssetTransaction. - GetRawData().GetContract()) == 0 { - log.Fatalf("transfer asset error: invalid transaction") - } - - util.SignTransaction(transferAssetTransaction, ownerKey) - - result, err := g.Client.BroadcastTransaction(ctx, - transferAssetTransaction) - - if err != nil { - log.Fatalf("transfer asset error: %v", err) - } - - return result -} - -func (g *GrpcClient) ParticipateAssetIssue(ownerKey *ecdsa.PrivateKey, - toAddress, - assetName string, amount int64) *api.Return { - - participateAssetIssueContract := new(core.ParticipateAssetIssueContract) - participateAssetIssueContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey. - PublicKey).Bytes() - participateAssetIssueContract.ToAddress = base58.DecodeCheck(toAddress) - participateAssetIssueContract.AssetName = []byte(assetName) - participateAssetIssueContract.Amount = amount - - participateAssetIssueTransaction, err := g.Client.ParticipateAssetIssue( - context. - Background(), participateAssetIssueContract) - - if err != nil { - log.Fatalf("participate asset error: %v", err) - } - - if participateAssetIssueTransaction == nil || len(participateAssetIssueTransaction. - GetRawData().GetContract()) == 0 { - log.Fatalf("participate asset error: invalid transaction") - } - - util.SignTransaction(participateAssetIssueTransaction, ownerKey) - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - result, err := g.Client.BroadcastTransaction(ctx, - participateAssetIssueTransaction) - - if err != nil { - log.Fatalf("participate asset error: %v", err) - } - - return result -} - -func (g *GrpcClient) CreateWitness(ownerKey *ecdsa.PrivateKey, - urlStr string) *api.Return { - - witnessCreateContract := new(core.WitnessCreateContract) - witnessCreateContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey. - PublicKey).Bytes() - witnessCreateContract.Url = []byte(urlStr) - - witnessCreateTransaction, err := g.Client.CreateWitness(context. - Background(), witnessCreateContract) - - if err != nil { - log.Fatalf("create witness error: %v", err) - } - - if witnessCreateTransaction == nil || len(witnessCreateTransaction. - GetRawData().GetContract()) == 0 { - log.Fatalf("create witness error: invalid transaction") - } - - util.SignTransaction(witnessCreateTransaction, ownerKey) - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - result, err := g.Client.BroadcastTransaction(ctx, - witnessCreateTransaction) - - if err != nil { - log.Fatalf("create witness error: %v", err) - } - - return result -} - -func (g *GrpcClient) UpdateWitness(ownerKey *ecdsa.PrivateKey, - urlStr string) *api.Return { - - witnessUpdateContract := new(core.WitnessUpdateContract) - witnessUpdateContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey. - PublicKey).Bytes() - witnessUpdateContract.UpdateUrl = []byte(urlStr) - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - witnessUpdateTransaction, err := g.Client.UpdateWitness(ctx, - witnessUpdateContract) - - if err != nil { - log.Fatalf("update witness error: %v", err) - } - - if witnessUpdateTransaction == nil || len(witnessUpdateTransaction. - GetRawData().GetContract()) == 0 { - log.Fatalf("update witness error: invalid transaction") - } - - util.SignTransaction(witnessUpdateTransaction, ownerKey) - - result, err := g.Client.BroadcastTransaction(ctx, - witnessUpdateTransaction) - - if err != nil { - log.Fatalf("update witness error: %v", err) - } - - return result -} - -func (g *GrpcClient) VoteWitnessAccount(ownerKey *ecdsa.PrivateKey, - witnessMap map[string]string) *api.Return { - - voteWitnessContract := new(core.VoteWitnessContract) - voteWitnessContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey. - PublicKey).Bytes() - - for key, value := range witnessMap { - witnessAddress := base58.DecodeCheck(key) - voteCount, err := strconv.ParseInt(value, 64, 10) - - if err != nil { - log.Fatalf("vote witness account error: %v", err) - } - - vote := new(core.VoteWitnessContract_Vote) - vote.VoteAddress = witnessAddress - vote.VoteCount = voteCount - voteWitnessContract.Votes = append(voteWitnessContract.Votes, vote) - } - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - voteWitnessTransaction, err := g.Client.VoteWitnessAccount(ctx, - voteWitnessContract) - - if err != nil { - log.Fatalf("vote witness account error: %v", err) - } - - if voteWitnessTransaction == nil || len(voteWitnessTransaction. - GetRawData().GetContract()) == 0 { - log.Fatalf("vote witness account error: invalid transaction") - } - - util.SignTransaction(voteWitnessTransaction, ownerKey) - - result, err := g.Client.BroadcastTransaction(ctx, - voteWitnessTransaction) - - if err != nil { - log.Fatalf("vote witness account error: %v", err) - } - - return result -} - -func (g *GrpcClient) UnfreezeAsset(ownerKey *ecdsa.PrivateKey, - urlStr string) *api.Return { - - unfreezeAssetContract := new(core.UnfreezeAssetContract) - unfreezeAssetContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey. - PublicKey).Bytes() - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - unfreezeAssetTransaction, err := g.Client.UnfreezeAsset(ctx, - unfreezeAssetContract) - - if err != nil { - log.Fatalf("unfreeze asset error: %v", err) - } - - if unfreezeAssetTransaction == nil || len(unfreezeAssetTransaction. - GetRawData().GetContract()) == 0 { - log.Fatalf("unfreeze asset error: invalid transaction") - } - - util.SignTransaction(unfreezeAssetTransaction, ownerKey) - - result, err := g.Client.BroadcastTransaction(ctx, - unfreezeAssetTransaction) - - if err != nil { - log.Fatalf("unfreeze asset error: %v", err) - } - - return result -} - -func (g *GrpcClient) WithdrawBalance(ownerKey *ecdsa.PrivateKey, - urlStr string) *api.Return { - - withdrawBalanceContract := new(core.WithdrawBalanceContract) - withdrawBalanceContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey. - PublicKey).Bytes() - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - withdrawBalanceTransaction, err := g.Client.WithdrawBalance(ctx, - withdrawBalanceContract) - - if err != nil { - log.Fatalf("withdraw balance error: %v", err) - } - - if withdrawBalanceTransaction == nil || len(withdrawBalanceTransaction. - GetRawData().GetContract()) == 0 { - log.Fatalf("withdraw balance error: invalid transaction") - } - - util.SignTransaction(withdrawBalanceTransaction, ownerKey) - - result, err := g.Client.BroadcastTransaction(ctx, - withdrawBalanceTransaction) - - if err != nil { - log.Fatalf("withdraw balance error: %v", err) - } - - return result -} diff --git a/util/block.go b/util/block.go deleted file mode 100644 index 009d7419..00000000 --- a/util/block.go +++ /dev/null @@ -1,23 +0,0 @@ -package util - -import ( - "crypto/sha256" - "github.com/fbsobreira/gotron/core" - "github.com/golang/protobuf/proto" - "log" -) - -func GetBlockHash(block core.Block) []byte { - rawData := block.BlockHeader.RawData - - rawDataBytes, err := proto.Marshal(rawData) - if err != nil { - log.Fatalln(err.Error()) - } - - h256 := sha256.New() - h256.Write(rawDataBytes) - blockHash := h256.Sum(nil) - - return blockHash -} diff --git a/util/transaction.go b/util/transaction.go deleted file mode 100644 index 1da08d52..00000000 --- a/util/transaction.go +++ /dev/null @@ -1,37 +0,0 @@ -package util - -import ( - "crypto/ecdsa" - "crypto/sha256" - "github.com/fbsobreira/gotron/common/crypto" - "github.com/fbsobreira/gotron/core" - "github.com/golang/protobuf/proto" - "log" - "time" -) - -func SignTransaction(transaction *core.Transaction, key *ecdsa.PrivateKey) { - transaction.GetRawData().Timestamp = time.Now().UnixNano() / 1000000 - - rawData, err := proto.Marshal(transaction.GetRawData()) - - if err != nil { - log.Fatalf("sign transaction error: %v", err) - } - - h256h := sha256.New() - h256h.Write(rawData) - hash := h256h.Sum(nil) - - contractList := transaction.GetRawData().GetContract() - - for range contractList { - signature, err := crypto.Sign(hash, key) - - if err != nil { - log.Fatalf("sign transaction error: %v", err) - } - - transaction.Signature = append(transaction.Signature, signature) - } -} diff --git a/util/util/completeTx.pb.go b/util/util/completeTx.pb.go deleted file mode 100644 index 97fc3e7c..00000000 --- a/util/util/completeTx.pb.go +++ /dev/null @@ -1,110 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: util/completeTx.proto - -package protocol_util - -import ( - api "api" - core "core" - fmt "fmt" - math "math" - - proto "github.com/golang/protobuf/proto" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -type ProtoCompleteTransaction struct { - BlockNumber uint64 `protobuf:"varint,1,opt,name=BlockNumber,proto3" json:"BlockNumber,omitempty"` - BlockTime uint64 `protobuf:"varint,2,opt,name=BlockTime,proto3" json:"BlockTime,omitempty"` - Tx *api.TransactionExtention `protobuf:"bytes,3,opt,name=Tx,proto3" json:"Tx,omitempty"` - Info *core.TransactionInfo `protobuf:"bytes,4,opt,name=Info,proto3" json:"Info,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ProtoCompleteTransaction) Reset() { *m = ProtoCompleteTransaction{} } -func (m *ProtoCompleteTransaction) String() string { return proto.CompactTextString(m) } -func (*ProtoCompleteTransaction) ProtoMessage() {} -func (*ProtoCompleteTransaction) Descriptor() ([]byte, []int) { - return fileDescriptor_ad72d1b67bea25c6, []int{0} -} - -func (m *ProtoCompleteTransaction) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ProtoCompleteTransaction.Unmarshal(m, b) -} -func (m *ProtoCompleteTransaction) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ProtoCompleteTransaction.Marshal(b, m, deterministic) -} -func (m *ProtoCompleteTransaction) XXX_Merge(src proto.Message) { - xxx_messageInfo_ProtoCompleteTransaction.Merge(m, src) -} -func (m *ProtoCompleteTransaction) XXX_Size() int { - return xxx_messageInfo_ProtoCompleteTransaction.Size(m) -} -func (m *ProtoCompleteTransaction) XXX_DiscardUnknown() { - xxx_messageInfo_ProtoCompleteTransaction.DiscardUnknown(m) -} - -var xxx_messageInfo_ProtoCompleteTransaction proto.InternalMessageInfo - -func (m *ProtoCompleteTransaction) GetBlockNumber() uint64 { - if m != nil { - return m.BlockNumber - } - return 0 -} - -func (m *ProtoCompleteTransaction) GetBlockTime() uint64 { - if m != nil { - return m.BlockTime - } - return 0 -} - -func (m *ProtoCompleteTransaction) GetTx() *api.TransactionExtention { - if m != nil { - return m.Tx - } - return nil -} - -func (m *ProtoCompleteTransaction) GetInfo() *core.TransactionInfo { - if m != nil { - return m.Info - } - return nil -} - -func init() { - proto.RegisterType((*ProtoCompleteTransaction)(nil), "protocol.util.ProtoCompleteTransaction") -} - -func init() { proto.RegisterFile("util/completeTx.proto", fileDescriptor_ad72d1b67bea25c6) } - -var fileDescriptor_ad72d1b67bea25c6 = []byte{ - // 197 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2d, 0x2d, 0xc9, 0xcc, - 0xd1, 0x4f, 0xce, 0xcf, 0x2d, 0xc8, 0x49, 0x2d, 0x49, 0x0d, 0xa9, 0xd0, 0x2b, 0x28, 0xca, 0x2f, - 0xc9, 0x17, 0xe2, 0x05, 0x53, 0xc9, 0xf9, 0x39, 0x7a, 0x20, 0x79, 0x29, 0x31, 0x18, 0x57, 0x3f, - 0xb1, 0x20, 0x13, 0x84, 0x21, 0xca, 0xa4, 0xf8, 0x93, 0xf3, 0x8b, 0x52, 0xf5, 0x43, 0x8a, 0xf2, - 0xf3, 0x20, 0x02, 0x4a, 0x3b, 0x19, 0xb9, 0x24, 0x02, 0x40, 0x2c, 0x67, 0x98, 0x89, 0x45, 0x89, - 0x79, 0xc5, 0x89, 0xc9, 0x25, 0x99, 0xf9, 0x79, 0x42, 0x0a, 0x5c, 0xdc, 0x4e, 0x39, 0xf9, 0xc9, - 0xd9, 0x7e, 0xa5, 0xb9, 0x49, 0xa9, 0x45, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x2c, 0x41, 0xc8, 0x42, - 0x42, 0x32, 0x5c, 0x9c, 0x60, 0x6e, 0x48, 0x66, 0x6e, 0xaa, 0x04, 0x13, 0x58, 0x1e, 0x21, 0x20, - 0xa4, 0xc7, 0xc5, 0x14, 0x52, 0x21, 0xc1, 0xac, 0xc0, 0xa8, 0xc1, 0x6d, 0x24, 0xa7, 0x07, 0x77, - 0x21, 0x92, 0x15, 0xae, 0x15, 0x25, 0xa9, 0x79, 0x20, 0x46, 0x10, 0x53, 0x48, 0x85, 0x90, 0x2e, - 0x17, 0x8b, 0x67, 0x5e, 0x5a, 0xbe, 0x04, 0x0b, 0x58, 0x87, 0x24, 0x56, 0x1d, 0x20, 0x05, 0x41, - 0x60, 0x65, 0x49, 0x6c, 0x60, 0x79, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7c, 0xcb, 0xb7, - 0x14, 0x13, 0x01, 0x00, 0x00, -}