From 874fc4b6eccc22594815cba329d4e4f08e9800af Mon Sep 17 00:00:00 2001 From: Fernando Sobreira Date: Fri, 17 Feb 2023 10:38:21 -0500 Subject: [PATCH] Improve Tests & Sign Message (#52) * replace deprecate protobuf package * . * improve tx parser * contract tests/examples * Account sign and verify message --- cmd/subcommands/account.go | 85 ++++++++- cmd/subcommands/bc.go | 256 +++++++++------------------ go.mod | 8 +- go.sum | 16 +- pkg/client/account.go | 2 +- pkg/client/assets.go | 2 +- pkg/client/bank.go | 2 +- pkg/client/client_test.go | 59 ++++++ pkg/client/contracts_test.go | 93 ++++++++++ pkg/client/exchange.go | 2 +- pkg/client/network.go | 6 +- pkg/client/proposal.go | 2 +- pkg/client/transaction/controller.go | 2 +- pkg/client/transfer.go | 2 +- pkg/client/trc20.go | 20 ++- pkg/client/trc20_test.go | 23 +++ pkg/client/witnesses.go | 2 +- pkg/common/hash.go | 8 + pkg/keystore/account.go | 61 +++++-- pkg/keystore/wallet.go | 4 +- 20 files changed, 439 insertions(+), 216 deletions(-) create mode 100755 pkg/client/client_test.go create mode 100755 pkg/client/contracts_test.go create mode 100644 pkg/client/trc20_test.go diff --git a/cmd/subcommands/account.go b/cmd/subcommands/account.go index 9b224aa16..8f628538a 100644 --- a/cmd/subcommands/account.go +++ b/cmd/subcommands/account.go @@ -1,6 +1,7 @@ package cmd import ( + "encoding/hex" "encoding/json" "fmt" "math" @@ -53,6 +54,7 @@ func accountSub() []*cobra.Command { result["balance"] = float64(acc.GetBalance()) / 1000000 result["allowance"] = float64(acc.GetAllowance()) / 1000000 result["rewards"] = float64(rewards) / 1000000 + asJSON, _ := json.Marshal(result) fmt.Println(common.JSONPrettyFormat(string(asJSON))) return nil @@ -607,7 +609,88 @@ func accountSub() []*cobra.Command { cmdPermission.Flags().StringSliceVar(&permissionList, "allow", []string{}, "TYPE:THRESHOLD:ADDRESS1-WEIGHT+ADDRESS2-WEIGHT") - return []*cobra.Command{cmdBalance, cmdActivate, cmdSend, cmdAddress, cmdInfo, cmdWithdraw, cmdFreeze, cmdVote, cmdPermission} + var useFixedLength bool + var hashMessage bool + + cmdSign := &cobra.Command{ + Use: "sign", + Short: "sign message", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + if signerAddress.String() == "" { + return fmt.Errorf("no signer specified") + } + + message := []byte(args[0]) + if hashMessage { + message = common.Keccak256([]byte(message)) + } + + var signature []byte + if useLedgerWallet { + // TODO: + // account := keystore.Account{Address: signerAddress.GetAddress()} + } else { + ks, acct, err := store.UnlockedKeystore(signerAddress.String(), passphrase) + if err != nil { + return err + } + signature, err = ks.Wallets()[0].SignText(*acct, []byte(message), useFixedLength) + if err != nil { + return err + } + } + + result := make(map[string]interface{}) + result["Signer"] = signerAddress.String() + result["Message"] = args[0] + result["Signature"] = hex.EncodeToString(signature) + asJSON, _ := json.Marshal(result) + fmt.Println(common.JSONPrettyFormat(string(asJSON))) + return nil + }, + } + cmdSign.Flags().BoolVar(&useFixedLength, "useFixedLength", false, "--useFixedLength=true") + cmdSign.Flags().BoolVar(&hashMessage, "hashMessage", false, "--hashMessage=true") + + cmdVerify := &cobra.Command{ + Use: "verify", + Short: "verify message signature", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + + message := []byte(args[0]) + if hashMessage { + message = common.Keccak256([]byte(message)) + } + + // compute message hash + hash := keystore.TextHash(message, useFixedLength) + signature, err := hex.DecodeString(args[1]) + if err != nil { + fmt.Println("Invalid signature") + return err + } + + addr, err := keystore.RecoverPubkey(hash, signature) + if err != nil { + return err + } + + result := make(map[string]interface{}) + result["Message"] = args[0] + result["Signature"] = args[1] + result["Signer"] = addr.String() + + asJSON, _ := json.Marshal(result) + fmt.Println(common.JSONPrettyFormat(string(asJSON))) + return nil + }, + } + cmdVerify.Flags().BoolVar(&useFixedLength, "useFixedLength", false, "--useFixedLength=true") + cmdVerify.Flags().BoolVar(&hashMessage, "hashMessage", false, "--hashMessage=true") + + return []*cobra.Command{cmdBalance, cmdActivate, cmdSend, cmdAddress, cmdInfo, cmdWithdraw, cmdFreeze, cmdVote, cmdPermission, cmdSign, cmdVerify} } func init() { diff --git a/cmd/subcommands/bc.go b/cmd/subcommands/bc.go index 6a30d98e5..51a07a87b 100644 --- a/cmd/subcommands/bc.go +++ b/cmd/subcommands/bc.go @@ -3,14 +3,15 @@ package cmd import ( "encoding/json" "fmt" + "strings" "time" "github.com/fatih/structs" "github.com/fbsobreira/gotron-sdk/pkg/address" "github.com/fbsobreira/gotron-sdk/pkg/common" "github.com/fbsobreira/gotron-sdk/pkg/proto/core" - "github.com/golang/protobuf/ptypes" "github.com/spf13/cobra" + "google.golang.org/protobuf/reflect/protoreflect" ) var () @@ -107,210 +108,94 @@ func bcSub() []*cobra.Command { result["contractName"] = contract.Type.String() //parse contract + var c interface{} switch contract.Type { case core.Transaction_Contract_AccountCreateContract: - var c core.AccountCreateContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.AccountCreateContract{} case core.Transaction_Contract_TransferContract: - var c core.TransferContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.TransferContract{} case core.Transaction_Contract_TransferAssetContract: - var c core.TransferAssetContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.TransferAssetContract{} case core.Transaction_Contract_VoteWitnessContract: - var c core.VoteWitnessContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.VoteWitnessContract{} case core.Transaction_Contract_WitnessCreateContract: - var c core.WitnessCreateContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.WitnessCreateContract{} + case core.Transaction_Contract_WitnessUpdateContract: + c = &core.WitnessUpdateContract{} case core.Transaction_Contract_AssetIssueContract: - var c core.AssetIssueContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.AssetIssueContract{} case core.Transaction_Contract_ParticipateAssetIssueContract: - var c core.ParticipateAssetIssueContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.ParticipateAssetIssueContract{} case core.Transaction_Contract_AccountUpdateContract: - var c core.AccountUpdateContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.AccountUpdateContract{} case core.Transaction_Contract_FreezeBalanceContract: - var c core.FreezeBalanceContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.FreezeBalanceContract{} case core.Transaction_Contract_UnfreezeBalanceContract: - var c core.UnfreezeBalanceContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.UnfreezeBalanceContract{} case core.Transaction_Contract_WithdrawBalanceContract: - var c core.WithdrawBalanceContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.WithdrawBalanceContract{} case core.Transaction_Contract_UnfreezeAssetContract: - var c core.UnfreezeAssetContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.UnfreezeAssetContract{} case core.Transaction_Contract_UpdateAssetContract: - var c core.UpdateAssetContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) - + c = &core.UpdateAssetContract{} case core.Transaction_Contract_ProposalCreateContract: - var c core.ProposalCreateContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.ProposalCreateContract{} case core.Transaction_Contract_ProposalApproveContract: - var c core.ProposalApproveContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.ProposalApproveContract{} case core.Transaction_Contract_ProposalDeleteContract: - var c core.ProposalDeleteContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.ProposalDeleteContract{} case core.Transaction_Contract_SetAccountIdContract: - var c core.SetAccountIdContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.SetAccountIdContract{} case core.Transaction_Contract_CustomContract: - return fmt.Errorf("Tx inconsistent") + return fmt.Errorf("proto unmarshal any: %s", "customContract") case core.Transaction_Contract_CreateSmartContract: - var c core.CreateSmartContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.CreateSmartContract{} case core.Transaction_Contract_TriggerSmartContract: - var c core.TriggerSmartContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.TriggerSmartContract{} + case core.Transaction_Contract_GetContract: + return fmt.Errorf("proto unmarshal any: %s", "getContract") case core.Transaction_Contract_UpdateSettingContract: - var c core.UpdateSettingContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.UpdateSettingContract{} case core.Transaction_Contract_ExchangeCreateContract: - var c core.ExchangeCreateContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.ExchangeCreateContract{} case core.Transaction_Contract_ExchangeInjectContract: - var c core.ExchangeInjectContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.ExchangeInjectContract{} case core.Transaction_Contract_ExchangeWithdrawContract: - var c core.ExchangeWithdrawContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.ExchangeWithdrawContract{} case core.Transaction_Contract_ExchangeTransactionContract: - var c core.ExchangeTransactionContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.ExchangeTransactionContract{} case core.Transaction_Contract_UpdateEnergyLimitContract: - var c core.UpdateEnergyLimitContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.UpdateEnergyLimitContract{} case core.Transaction_Contract_AccountPermissionUpdateContract: - var c core.AccountPermissionUpdateContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.AccountPermissionUpdateContract{} case core.Transaction_Contract_ClearABIContract: - var c core.ClearABIContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.ClearABIContract{} case core.Transaction_Contract_UpdateBrokerageContract: - var c core.UpdateBrokerageContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.UpdateBrokerageContract{} case core.Transaction_Contract_ShieldedTransferContract: - var c core.ShieldedTransferContract - if err = ptypes.UnmarshalAny(contract.GetParameter(), &c); err != nil { - return fmt.Errorf("Tx inconsistent") - } - result["contract"] = structs.Map(c) + c = &core.ShieldedTransferContract{} + case core.Transaction_Contract_MarketSellAssetContract: + c = &core.MarketSellAssetContract{} + case core.Transaction_Contract_MarketCancelOrderContract: + c = &core.MarketCancelOrderContract{} + case core.Transaction_Contract_FreezeBalanceV2Contract: + c = &core.FreezeBalanceV2Contract{} + case core.Transaction_Contract_UnfreezeBalanceV2Contract: + c = &core.UnfreezeBalanceV2Contract{} + case core.Transaction_Contract_WithdrawExpireUnfreezeContract: + c = &core.WithdrawExpireUnfreezeContract{} + case core.Transaction_Contract_DelegateResourceContract: + c = &core.DelegateResourceContract{} + case core.Transaction_Contract_UnDelegateResourceContract: + c = &core.UnDelegateResourceContract{} default: - return fmt.Errorf("Tx inconsistent") - } - - c := result["contract"].(map[string]interface{}) - delete(c, "XXX_NoUnkeyedLiteral") - delete(c, "XXX_sizecache") - delete(c, "XXX_unrecognized") - if v, ok := c["OwnerAddress"]; ok && len(v.([]uint8)) > 0 { - c["OwnerAddress"] = address.Address(v.([]uint8)).String() - } - if v, ok := c["ReceiverAddress"]; ok && len(v.([]uint8)) > 0 { - c["ReceiverAddress"] = address.Address(v.([]uint8)).String() - } - if v, ok := c["ToAddress"]; ok && len(v.([]uint8)) > 0 { - c["ToAddress"] = address.Address(v.([]uint8)).String() + return fmt.Errorf("proto unmarshal any: %+w", err) } - if v, ok := c["Votes"]; ok { - votes := make(map[string]int64) - for _, d := range v.([]interface{}) { - dP := d.(map[string]interface{}) - votes[address.Address(dP["VoteAddress"].([]uint8)).String()] = dP["VoteCount"].(int64) - } - c["Votes"] = votes + if err = contract.GetParameter().UnmarshalTo(c.(protoreflect.ProtoMessage)); err != nil { + return fmt.Errorf("proto unmarshal any: %+w", err) } + result["contract"] = parseContractHumanReadable(structs.Map(c)) asJSON, _ := json.Marshal(result) fmt.Println(common.JSONPrettyFormat(string(asJSON))) @@ -321,6 +206,37 @@ func bcSub() []*cobra.Command { return []*cobra.Command{cmdNode, cmdMT, cmdTX} } +func parseContractHumanReadable(ck map[string]interface{}) map[string]interface{} { + // Addresses fields + addresses := map[string]bool{ + "OwnerAddress": true, + "ReceiverAddress": true, + "ToAddress": true, + "ContractAddress": true, + } + for f, d := range ck { + if strings.HasPrefix(f, "XXX_") { + delete(ck, f) + } + + // convert addresses + if addresses[f] { + ck[f] = address.Address(d.([]uint8)).String() + } + } + + if v, ok := ck["Votes"]; ok { + votes := make(map[string]int64) + for _, d := range v.([]interface{}) { + dP := d.(map[string]interface{}) + votes[address.Address(dP["VoteAddress"].([]uint8)).String()] = dP["VoteCount"].(int64) + } + ck["Votes"] = votes + } + + return ck +} + func init() { cmdBC := &cobra.Command{ Use: "bc", diff --git a/go.mod b/go.mod index 78846a6e8..3c74f4301 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,6 @@ require ( github.com/ethereum/go-ethereum v1.10.11 github.com/fatih/color v1.9.0 github.com/fatih/structs v1.1.0 - github.com/golang/protobuf v1.4.3 github.com/karalabe/hid v1.0.0 github.com/mitchellh/go-homedir v1.1.0 github.com/pborman/uuid v1.2.1 @@ -23,7 +22,7 @@ require ( golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 google.golang.org/grpc v1.37.0 - google.golang.org/protobuf v1.25.0 + google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v2 v2.4.0 ) @@ -31,6 +30,7 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-stack/stack v1.8.0 // indirect + github.com/golang/protobuf v1.5.0 // indirect github.com/google/uuid v1.1.5 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/mattn/go-colorable v0.1.8 // indirect @@ -38,11 +38,11 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.0.1 // indirect github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/pflag v1.0.3 // indirect go.uber.org/atomic v1.6.0 // indirect go.uber.org/multierr v1.5.0 // indirect golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect - golang.org/x/sys v0.0.0-20211102061401-a2f17f7b995c // indirect + golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912 // indirect golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect golang.org/x/text v0.3.6 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect diff --git a/go.sum b/go.sum index 129061fd8..1ad71f294 100644 --- a/go.sum +++ b/go.sum @@ -174,8 +174,9 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -189,8 +190,9 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -381,9 +383,8 @@ github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3 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/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= 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/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -554,9 +555,8 @@ golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912 h1:uCLL3g5wH2xjxVREVuAbP9JM5PPKjRbXKRa6IBjkzmU= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211102061401-a2f17f7b995c h1:QOfDMdrf/UwlVR0UBq2Mpr58UzNtvgJRXA4BgPfFACs= -golang.org/x/sys v0.0.0-20211102061401-a2f17f7b995c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -662,8 +662,10 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 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= diff --git a/pkg/client/account.go b/pkg/client/account.go index b66c777e1..c2035a088 100644 --- a/pkg/client/account.go +++ b/pkg/client/account.go @@ -10,7 +10,7 @@ import ( "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" + "google.golang.org/protobuf/proto" ) // GetAccount from BASE58 address diff --git a/pkg/client/assets.go b/pkg/client/assets.go index 56fe406f8..5f633b09a 100644 --- a/pkg/client/assets.go +++ b/pkg/client/assets.go @@ -9,7 +9,7 @@ import ( "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" + "google.golang.org/protobuf/proto" ) // GetAssetIssueByAccount list asset issued by account diff --git a/pkg/client/bank.go b/pkg/client/bank.go index ae19808b9..da7a1766d 100644 --- a/pkg/client/bank.go +++ b/pkg/client/bank.go @@ -6,7 +6,7 @@ import ( "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" + "google.golang.org/protobuf/proto" ) // FreezeBalance from base58 address diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go new file mode 100755 index 000000000..a3c3fa3be --- /dev/null +++ b/pkg/client/client_test.go @@ -0,0 +1,59 @@ +package client_test + +import ( + "crypto/sha256" + "encoding/hex" + "testing" + + "github.com/btcsuite/btcd/btcec" + "github.com/ethereum/go-ethereum/crypto" + "github.com/fbsobreira/gotron-sdk/pkg/client" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/protobuf/proto" +) + +func TestTRC20(t *testing.T) { + c := client.NewGrpcClient("") + err := c.Start(grpc.WithInsecure()) + require.Nil(t, err) + + value, err := c.TRC20GetDecimals("TN7EWmuVWrdehLwKGnU2rk42GWodbAXGUM") + require.Nil(t, err) + require.Equal(t, value.Int64(), int64(0)) + + value, err = c.TRC20GetDecimals("TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t") + require.Nil(t, err) + require.Equal(t, value.Int64(), int64(6)) +} + +func TestSend(t *testing.T) { + t.Skip() + fromAddress := "" + toAddress := "" + + privateKeyBytes, _ := hex.DecodeString("ABCD") + + c := client.NewGrpcClient("") + err := c.Start(grpc.WithInsecure()) + require.Nil(t, err) + tx, err := c.Transfer(fromAddress, toAddress, 1000) + require.Nil(t, err) + + rawData, err := proto.Marshal(tx.Transaction.GetRawData()) + require.Nil(t, err) + h256h := sha256.New() + h256h.Write(rawData) + hash := h256h.Sum(nil) + + // btcec.PrivKeyFromBytes only returns a secret key and public key + sk, _ := btcec.PrivKeyFromBytes(btcec.S256(), privateKeyBytes) + + signature, err := crypto.Sign(hash, sk.ToECDSA()) + require.Nil(t, err) + tx.Transaction.Signature = append(tx.Transaction.Signature, signature) + + result, err := c.Broadcast(tx.Transaction) + require.Nil(t, err) + require.NotNil(t, result) +} diff --git a/pkg/client/contracts_test.go b/pkg/client/contracts_test.go new file mode 100755 index 000000000..4dcca074a --- /dev/null +++ b/pkg/client/contracts_test.go @@ -0,0 +1,93 @@ +package client_test + +import ( + "encoding/hex" + "fmt" + "math/big" + "testing" + + "github.com/fbsobreira/gotron-sdk/pkg/abi" + "github.com/fbsobreira/gotron-sdk/pkg/client" + "github.com/fbsobreira/gotron-sdk/pkg/proto/api" + "github.com/fbsobreira/gotron-sdk/pkg/proto/core" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/protobuf/proto" +) + +func TestProtoParse(t *testing.T) { + raw := &core.TransactionRaw{} + + mb, _ := hex.DecodeString("0a020cd222081e6d180d0ea1be1340c082fc94c22e5a8e01081f1289010a31747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e54726967676572536d617274436f6e747261637412540a15419df085719e7e0bd5bf4fd1b2a6aed6afd2b8416d121541157a629d8e8d7d43218b83240afaa02e8c300b36222497a5d5b50000000000000000000000009df085719e7e0bd5bf4fd1b2a6aed6afd2b8416d7085c1f894c22e") + + proto.Unmarshal(mb, raw) + fmt.Printf("Raw: %+v\n", raw) + c := raw.GetContract()[0] + trig := &core.TriggerSmartContract{} + // recover + err := c.GetParameter().UnmarshalTo(trig) + require.Nil(t, err) + assert.Equal(t, hex.EncodeToString(trig.Data), "97a5d5b50000000000000000000000009df085719e7e0bd5bf4fd1b2a6aed6afd2b8416d") +} + +func TestGetAccount(t *testing.T) { + conn := client.NewGrpcClient("grpc.trongrid.io:50051") + err := conn.Start(grpc.WithInsecure()) + require.Nil(t, err) + + tx, err := conn.TriggerConstantContract("", + "TBvmoZWgmx3wqvJoDyejSXqWWogy6kCNGp", + "statusOf(address)", `[{"address": "TQNKDtPaeSSGhtbDAykLeHEpMpfUYmSuj1"}]`) + require.Nil(t, err) + + assert.Equal(t, api.Return_SUCCESS, tx.Result.Code) + + /*const abiJSON = `[{"outputs":[{"type":"address"}],"constant":true,"name":"kleverToken","stateMutability":"View","type":"Function"},{"name":"unpause","stateMutability":"Nonpayable","type":"Function"},{"outputs":[{"type":"bool"}],"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"isPauser","stateMutability":"View","type":"Function"},{"inputs":[{"name":"_to","type":"address"}],"name":"reclaimTRX","stateMutability":"Nonpayable","type":"Function"},{"outputs":[{"type":"bool"}],"constant":true,"name":"paused","stateMutability":"View","type":"Function"},{"name":"renouncePauser","stateMutability":"Nonpayable","type":"Function"},{"name":"renounceOwnership","stateMutability":"Nonpayable","type":"Function"},{"inputs":[{"name":"account","type":"address"}],"name":"addPauser","stateMutability":"Nonpayable","type":"Function"},{"name":"pause","stateMutability":"Nonpayable","type":"Function"},{"inputs":[{"name":"_token","type":"address"},{"name":"_to","type":"address"}],"name":"reclaimToken","stateMutability":"Nonpayable","type":"Function"},{"outputs":[{"type":"address"}],"constant":true,"name":"owner","stateMutability":"View","type":"Function"},{"outputs":[{"type":"bool"}],"constant":true,"name":"isOwner","stateMutability":"View","type":"Function"},{"outputs":[{"type":"uint256"}],"constant":true,"name":"totalMinted","stateMutability":"View","type":"Function"},{"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","stateMutability":"Nonpayable","type":"Function"},{"inputs":[{"name":"_kleverToken","type":"address"}],"stateMutability":"Nonpayable","type":"Constructor"},{"inputs":[{"name":"account","type":"address"}],"name":"Paused","type":"Event"},{"inputs":[{"name":"account","type":"address"}],"name":"Unpaused","type":"Event"},{"inputs":[{"indexed":true,"name":"account","type":"address"}],"name":"PauserAdded","type":"Event"},{"inputs":[{"indexed":true,"name":"account","type":"address"}],"name":"PauserRemoved","type":"Event"},{"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"Event"},{"inputs":[{"name":"APR","type":"uint32"}],"name":"SetAPR","type":"Event"},{"inputs":[{"indexed":true,"name":"account","type":"address"},{"name":"amount","type":"uint256"},{"name":"APR","type":"uint32"},{"name":"totalFrozen","type":"uint256"}],"name":"Freeze","type":"Event"},{"inputs":[{"indexed":true,"name":"account","type":"address"},{"name":"amount","type":"uint256"},{"name":"totalUnfrozen","type":"uint256"},{"name":"realizedInterest","type":"uint256"},{"name":"availableDate","type":"uint64"}],"name":"Unfreeze","type":"Event"},{"inputs":[{"indexed":true,"name":"account","type":"address"},{"name":"totalUnfrozen","type":"uint256"},{"name":"realizedInterest","type":"uint256"}],"name":"Withdraw","type":"Event"},{"inputs":[{"indexed":true,"name":"account","type":"address"},{"name":"totalInterest","type":"uint256"},{"name":"realizedInterest","type":"uint256"}],"name":"Claim","type":"Event"},{"inputs":[{"name":"timeInSec","type":"uint64"}],"name":"SetMinTimeToUnfreeze","type":"Event"},{"inputs":[{"name":"timeInSec","type":"uint64"}],"name":"SetUnfreezeDelay","type":"Event"},{"inputs":[{"name":"timeInSec","type":"uint64"}],"name":"SetClaimDelay","type":"Event"},{"outputs":[{"name":"total","type":"uint256"}],"constant":true,"name":"totalFrozen","stateMutability":"View","type":"Function"},{"outputs":[{"name":"value","type":"uint32"}],"constant":true,"name":"currentAPR","stateMutability":"View","type":"Function"},{"outputs":[{"name":"success","type":"bool"}],"inputs":[{"name":"value","type":"uint32"}],"name":"setAPR","stateMutability":"Nonpayable","type":"Function"},{"outputs":[{"name":"value","type":"uint64"}],"constant":true,"name":"minTimeToUnfreeze","stateMutability":"View","type":"Function"},{"outputs":[{"name":"value","type":"uint64"}],"constant":true,"name":"unfreezeDelay","stateMutability":"View","type":"Function"},{"outputs":[{"name":"value","type":"uint64"}],"constant":true,"name":"claimDelay","stateMutability":"View","type":"Function"},{"outputs":[{"name":"success","type":"bool"}],"inputs":[{"name":"timeInSec","type":"uint64"}],"name":"setMinTimeToUnfreeze","stateMutability":"Nonpayable","type":"Function"},{"outputs":[{"name":"success","type":"bool"}],"inputs":[{"name":"timeInSec","type":"uint64"}],"name":"setUnfreezeDelay","stateMutability":"Nonpayable","type":"Function"},{"outputs":[{"name":"success","type":"bool"}],"inputs":[{"name":"timeInSec","type":"uint64"}],"name":"setClaimDelay","stateMutability":"Nonpayable","type":"Function"},{"outputs":[{"name":"frozenAmount","type":"uint256"}],"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"frozenBalanceOf","stateMutability":"View","type":"Function"},{"outputs":[{"name":"unfrozenAmount","type":"uint256"},{"name":"availableOn","type":"uint64"}],"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"unfrozenBalanceOf","stateMutability":"View","type":"Function"},{"outputs":[{"name":"realizedAmount","type":"uint256"}],"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"realizedInterestOf","stateMutability":"View","type":"Function"},{"outputs":[{"name":"lastClaim","type":"uint64"}],"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"lastClaimOf","stateMutability":"View","type":"Function"},{"outputs":[{"name":"frozenDate","type":"uint64"}],"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"frozenDateOf","stateMutability":"View","type":"Function"},{"outputs":[{"name":"pendingAmount","type":"uint256"}],"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"pendingInterestOf","stateMutability":"View","type":"Function"},{"outputs":[{"name":"APR","type":"uint32"}],"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"aprOf","stateMutability":"View","type":"Function"},{"outputs":[{"name":"timestamp","type":"uint64"}],"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"canUnfreezeOn","stateMutability":"View","type":"Function"},{"outputs":[{"name":"frozen","type":"uint256"},{"name":"unfreezeAvailableOn","type":"uint64"},{"name":"frozenDate","type":"uint64"},{"name":"pendingInterest","type":"uint256"},{"name":"realizedInterest","type":"uint256"},{"name":"APR","type":"uint32"},{"name":"unfrozen","type":"uint256"},{"name":"availableOn","type":"uint64"},{"name":"lastClaim","type":"uint64"}],"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"statusOf","stateMutability":"View","type":"Function"},{"outputs":[{"name":"success","type":"bool"}],"inputs":[{"name":"amount","type":"uint256"}],"name":"freeze","stateMutability":"Nonpayable","type":"Function"},{"outputs":[{"name":"availableDate","type":"uint64"}],"name":"unfreeze","stateMutability":"Nonpayable","type":"Function"},{"outputs":[{"name":"interestValue","type":"uint256"},{"name":"unfrozenValue","type":"uint256"}],"name":"withdraw","stateMutability":"Nonpayable","type":"Function"},{"outputs":[{"name":"interest","type":"uint256"}],"name":"claim","stateMutability":"Nonpayable","type":"Function"}]` + a, _ := contract.JSONtoABI(abiJSON) + */ + a, err := conn.GetContractABI("TBvmoZWgmx3wqvJoDyejSXqWWogy6kCNGp") + require.Nil(t, err) + arg, err := abi.GetParser(a, "statusOf") + require.Nil(t, err) + fmt.Printf("\nContractABI ->>> %+v\n\n", a) + fmt.Printf("\nResult ->>> %s\n\n", hex.EncodeToString(tx.ConstantResult[0])) + result := map[string]interface{}{} + err = arg.UnpackIntoMap(result, tx.ConstantResult[0]) + fmt.Printf("\nUnpack Result ->>> %+v\n\n", result) + require.Nil(t, err) +} + +func TestGetAccount2(t *testing.T) { + conn := client.NewGrpcClient("grpc.trongrid.io:50051") + err := conn.Start(grpc.WithInsecure()) + require.Nil(t, err) + + tx, err := conn.GetAccountDetailed("TPpw7soPWEDQWXPCGUMagYPryaWrYR5b3b") + require.Nil(t, err) + fmt.Printf("%v", tx) +} + +func TestGetAccountMigrationContract(t *testing.T) { + conn := client.NewGrpcClient("grpc.trongrid.io:50051") + err := conn.Start(grpc.WithInsecure()) + require.Nil(t, err) + + tx, err := conn.TriggerConstantContract("TX8h6Df74VpJsXF6sTDz1QJsq3Ec8dABc3", + "TVoo62PAagTvNvZbB796YfZ7dWtqpPNxnL", + "frozenAmount(address)", `[{"address": "TX8h6Df74VpJsXF6sTDz1QJsq3Ec8dABc3"}]`) + require.Nil(t, err) + assert.Equal(t, api.Return_SUCCESS, tx.Result.Code) + + a, err := conn.GetContractABI("TVoo62PAagTvNvZbB796YfZ7dWtqpPNxnL") + require.Nil(t, err) + arg, err := abi.GetParser(a, "frozenAmount") + require.Nil(t, err) + fmt.Printf("\nContractABI ->>> %+v\n\n", a) + fmt.Printf("\nResult ->>> %s\n\n", hex.EncodeToString(tx.ConstantResult[0])) + + result := map[string]interface{}{} + err = arg.UnpackIntoMap(result, tx.ConstantResult[0]) + fmt.Println(result["amount"].(*big.Int).Int64()) + require.Nil(t, err) +} diff --git a/pkg/client/exchange.go b/pkg/client/exchange.go index a4fad3bb9..51492c488 100644 --- a/pkg/client/exchange.go +++ b/pkg/client/exchange.go @@ -7,7 +7,7 @@ import ( "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" + "google.golang.org/protobuf/proto" ) // ExchangeList of bancor TRC10, use page -1 to list all diff --git a/pkg/client/network.go b/pkg/client/network.go index e2cde9e97..d4f79d7fe 100644 --- a/pkg/client/network.go +++ b/pkg/client/network.go @@ -7,8 +7,8 @@ import ( "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" "go.uber.org/zap" + "google.golang.org/protobuf/proto" ) // ListNodes provides list of network nodes @@ -41,7 +41,7 @@ func (g *GrpcClient) TotalTransaction() (*api.NumberMessage, error) { new(api.EmptyMessage)) } -//GetTransactionByID returns transaction details by ID +// GetTransactionByID returns transaction details by ID func (g *GrpcClient) GetTransactionByID(id string) (*core.Transaction, error) { transactionID := new(api.BytesMessage) var err error @@ -64,7 +64,7 @@ func (g *GrpcClient) GetTransactionByID(id string) (*core.Transaction, error) { return nil, fmt.Errorf("transaction info not found") } -//GetTransactionInfoByID returns transaction receipt by ID +// GetTransactionInfoByID returns transaction receipt by ID func (g *GrpcClient) GetTransactionInfoByID(id string) (*core.TransactionInfo, error) { transactionID := new(api.BytesMessage) var err error diff --git a/pkg/client/proposal.go b/pkg/client/proposal.go index 04a4ad17a..ea150921f 100644 --- a/pkg/client/proposal.go +++ b/pkg/client/proposal.go @@ -6,7 +6,7 @@ import ( "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" + "google.golang.org/protobuf/proto" ) // ProposalsList return all network proposals diff --git a/pkg/client/transaction/controller.go b/pkg/client/transaction/controller.go index c2c5b3fef..d602d9495 100644 --- a/pkg/client/transaction/controller.go +++ b/pkg/client/transaction/controller.go @@ -12,7 +12,7 @@ import ( "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" + proto "google.golang.org/protobuf/proto" ) var ( diff --git a/pkg/client/transfer.go b/pkg/client/transfer.go index 7cdd50d78..fed9778ee 100644 --- a/pkg/client/transfer.go +++ b/pkg/client/transfer.go @@ -6,7 +6,7 @@ import ( "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" + "google.golang.org/protobuf/proto" ) // Transfer from to base58 address diff --git a/pkg/client/trc20.go b/pkg/client/trc20.go index 771a9a33a..bfd7ba2c3 100644 --- a/pkg/client/trc20.go +++ b/pkg/client/trc20.go @@ -46,10 +46,9 @@ func (g *GrpcClient) TRC20Call(from, contractAddress, data string, constant bool ContractAddress: contractDesc.Bytes(), Data: dataBytes, } - result := &api.TransactionExtention{} + var result *api.TransactionExtention if constant { result, err = g.triggerConstantContract(ct) - } else { result, err = g.triggerContract(ct, feeLimit) } @@ -69,7 +68,7 @@ func (g *GrpcClient) TRC20GetName(contractAddress string) (string, error) { if err != nil { return "", err } - data := common.ToHex(result.GetConstantResult()[0]) + data := common.BytesToHexString(result.GetConstantResult()[0]) return g.ParseTRC20StringProperty(data) } @@ -79,7 +78,7 @@ func (g *GrpcClient) TRC20GetSymbol(contractAddress string) (string, error) { if err != nil { return "", err } - data := common.ToHex(result.GetConstantResult()[0]) + data := common.BytesToHexString(result.GetConstantResult()[0]) return g.ParseTRC20StringProperty(data) } @@ -89,7 +88,7 @@ func (g *GrpcClient) TRC20GetDecimals(contractAddress string) (*big.Int, error) if err != nil { return nil, err } - data := common.ToHex(result.GetConstantResult()[0]) + data := common.BytesToHexString(result.GetConstantResult()[0]) return g.ParseTRC20NumericProperty(data) } @@ -105,7 +104,12 @@ func (g *GrpcClient) ParseTRC20NumericProperty(data string) (*big.Int, error) { return &n, nil } } - return nil, fmt.Errorf("Cannot parse %s", data) + + if len(data) == 0 { + return big.NewInt(0), nil + } + + return nil, fmt.Errorf("cannot parse %s", data) } // ParseTRC20StringProperty get string from data @@ -137,7 +141,7 @@ func (g *GrpcClient) ParseTRC20StringProperty(data string) (string, error) { } } } - return "", fmt.Errorf("Cannot parse %s,", data) + return "", fmt.Errorf("cannot parse %s,", data) } // TRC20ContractBalance get Address balance @@ -151,7 +155,7 @@ func (g *GrpcClient) TRC20ContractBalance(addr, contractAddress string) (*big.In if err != nil { return nil, err } - data := common.ToHex(result.GetConstantResult()[0]) + data := common.BytesToHexString(result.GetConstantResult()[0]) r, err := g.ParseTRC20NumericProperty(data) if err != nil { return nil, fmt.Errorf("contract address %s: %v", contractAddress, err) diff --git a/pkg/client/trc20_test.go b/pkg/client/trc20_test.go new file mode 100644 index 000000000..7690b6da5 --- /dev/null +++ b/pkg/client/trc20_test.go @@ -0,0 +1,23 @@ +package client_test + +import ( + "testing" + + "github.com/fbsobreira/gotron-sdk/pkg/client" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" +) + +func TestTRC20_Balance(t *testing.T) { + trc20Contract := "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t" // USDT + address := "TMuA6YqfCeX8EhbfYEg5y7S4DqzSJireY9" + + conn := client.NewGrpcClient("grpc.trongrid.io:50051") + err := conn.Start(grpc.WithInsecure()) + require.Nil(t, err) + + balance, err := conn.TRC20ContractBalance(address, trc20Contract) + assert.Nil(t, err) + assert.Greater(t, balance.Int64(), int64(0)) +} diff --git a/pkg/client/witnesses.go b/pkg/client/witnesses.go index e962c00bd..2894e306f 100644 --- a/pkg/client/witnesses.go +++ b/pkg/client/witnesses.go @@ -6,7 +6,7 @@ import ( "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" + "google.golang.org/protobuf/proto" ) // ListWitnesses return all witnesses diff --git a/pkg/common/hash.go b/pkg/common/hash.go index 521ebdbe6..5978f60e0 100644 --- a/pkg/common/hash.go +++ b/pkg/common/hash.go @@ -3,6 +3,8 @@ package common import ( "fmt" "math/big" + + "golang.org/x/crypto/sha3" ) const ( @@ -65,3 +67,9 @@ func (h *Hash) SetBytes(b []byte) { copy(h[HashLength-len(b):], b) } + +func Keccak256(msg []byte) []byte { + hasher := sha3.NewLegacyKeccak256() + hasher.Write(msg) + return hasher.Sum(nil) +} diff --git a/pkg/keystore/account.go b/pkg/keystore/account.go index 9b8e9bcfa..f31cd73aa 100644 --- a/pkg/keystore/account.go +++ b/pkg/keystore/account.go @@ -1,11 +1,15 @@ package keystore import ( + "crypto/ecdsa" + "crypto/elliptic" "fmt" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/secp256k1" "github.com/fbsobreira/gotron-sdk/pkg/address" + "github.com/fbsobreira/gotron-sdk/pkg/common" "github.com/fbsobreira/gotron-sdk/pkg/proto/core" - "golang.org/x/crypto/sha3" ) type DerivationPath []uint32 @@ -88,7 +92,7 @@ type Wallet interface { // 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) + SignText(account Account, text []byte, useFixedLength ...bool) ([]byte, error) // SignTextWithPassphrase is identical to Signtext, but also takes a password SignTextWithPassphrase(account Account, passphrase string, hash []byte) ([]byte, error) @@ -136,25 +140,56 @@ 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}). +// The hash is calulcated as: +// keccak256("\x19TRON 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) +func TextHash(data []byte, useFixedLength ...bool) []byte { + hash, _ := TextAndHash(data, useFixedLength...) 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}). +// The hash is calulcated as: +// keccak256("\x19TRON 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 +func TextAndHash(data []byte, useFixedLength ...bool) ([]byte, string) { + length := len(data) + if len(useFixedLength) > 0 && useFixedLength[0] { + length = 32 + } + + msg := fmt.Sprintf("\x19TRON Signed Message:\n%d%s", length, string(data)) + return common.Keccak256([]byte(msg)), msg +} + +func UnmarshalPublic(pbk []byte) (*ecdsa.PublicKey, error) { + x, y := elliptic.Unmarshal(crypto.S256(), pbk) + if x == nil { + return nil, fmt.Errorf("invalid publickey") + } + + return &ecdsa.PublicKey{Curve: crypto.S256(), X: x, Y: y}, nil +} + +func RecoverPubkey(hash []byte, signature []byte) (address.Address, error) { + + if signature[64] >= 27 { + signature[64] -= 27 + } + + sigPublicKey, err := secp256k1.RecoverPubkey(hash, signature) + if err != nil { + return nil, err + } + pubKey, err := UnmarshalPublic(sigPublicKey) + if err != nil { + return nil, err + } + + addr := address.PubkeyToAddress(*pubKey) + return addr, nil } diff --git a/pkg/keystore/wallet.go b/pkg/keystore/wallet.go index f8a10413c..6a796c7c7 100644 --- a/pkg/keystore/wallet.go +++ b/pkg/keystore/wallet.go @@ -101,8 +101,8 @@ func (w *keystoreWallet) SignDataWithPassphrase(acc Account, passphrase, mimeTyp 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)) +func (w *keystoreWallet) SignText(acc Account, text []byte, useFixedLength ...bool) ([]byte, error) { + return w.signHash(acc, TextHash(text, useFixedLength...)) } // SignTextWithPassphrase implements Wallet, attempting to sign the