From ad76d18427d610bc6aa1d5822d0efb36592fb323 Mon Sep 17 00:00:00 2001 From: Fernando Sobreira Date: Thu, 23 Apr 2020 13:30:46 -0300 Subject: [PATCH 1/8] Add error returns --- common/base58/base58.go | 14 +- common/base58/base58_test.go | 5 +- controllers/account.go | 11 +- controllers/assetissue.go | 5 +- controllers/numbermessage.go | 5 +- models/account.go | 30 +++- models/accountnetmessage.go | 11 +- models/assetissue.go | 18 ++- models/contract/accountcreatecontract.go | 24 --- models/numbermessage.go | 12 +- service/client.go | 192 ++++++++++------------- 11 files changed, 158 insertions(+), 169 deletions(-) delete mode 100644 models/contract/accountcreatecontract.go diff --git a/common/base58/base58.go b/common/base58/base58.go index 838508e1..1fb36c12 100644 --- a/common/base58/base58.go +++ b/common/base58/base58.go @@ -2,8 +2,9 @@ package base58 import ( "crypto/sha256" + "fmt" + "github.com/shengdoushi/base58" - "log" ) var tronAlphabet = base58.NewAlphabet("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") @@ -31,15 +32,15 @@ func Decode(input string) ([]byte, error) { return base58.Decode(input, tronAlphabet) } -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 +57,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/base58/base58_test.go b/common/base58/base58_test.go index 72c044af..a191baaf 100644 --- a/common/base58/base58_test.go +++ b/common/base58/base58_test.go @@ -1,16 +1,17 @@ package base58 import ( - "github.com/fbsobreira/gotron/common/hexutil" "strings" "testing" + + "github.com/fbsobreira/gotron/common/hexutil" ) func TestEncode(t *testing.T) { } func TestDecodeCheck(t *testing.T) { - decodeBytes := DecodeCheck("27ZESitosJfKouTBrGg6Nk5yEjnJHXMbkZp") + decodeBytes, _ := DecodeCheck("27ZESitosJfKouTBrGg6Nk5yEjnJHXMbkZp") decode := hexutil.Encode(decodeBytes) diff --git a/controllers/account.go b/controllers/account.go index a7e9739f..edf644b4 100644 --- a/controllers/account.go +++ b/controllers/account.go @@ -4,8 +4,8 @@ import ( "github.com/fbsobreira/gotron/models" "encoding/json" + "github.com/astaxie/beego" - "github.com/fbsobreira/gotron/models/contract" ) // Operations about Account @@ -41,7 +41,10 @@ func (a *AccountController) Get() { func (a *AccountController) NetMessage() { address := a.GetString(":address") if address != "" { - accountNetMessage := models.GetAccountNet(address) + accountNetMessage, err := models.GetAccountNet(address) + if err != nil { + a.Abort("500") + } a.Data["json"] = accountNetMessage } a.ServeJSON() @@ -52,13 +55,13 @@ func (a *AccountController) NetMessage() { // @Param accountaddress body string true // @router /create [post] func (a *AccountController) CreateAccount() { - var accountCreateContract contract.AccountCreateContract + var accountCreateContract models.AccountCreateContract err := json.Unmarshal(a.Ctx.Input.RequestBody, &accountCreateContract) if err != nil { a.Data["json"] = err.Error() } else { - transaction, err := contract.CreateAccount(accountCreateContract) + transaction, err := models.CreateAccount(accountCreateContract) if err != nil { a.Data["json"] = err.Error() diff --git a/controllers/assetissue.go b/controllers/assetissue.go index 6bb5a33b..32243bb8 100644 --- a/controllers/assetissue.go +++ b/controllers/assetissue.go @@ -20,7 +20,10 @@ type AssetIssueController struct { func (i *AssetIssueController) Address() { address := i.GetString(":address") if address != "" { - assetIssueList := models.GetAssetIssueAccount(address) + assetIssueList, err := models.GetAssetIssueAccount(address) + if err != nil { + i.Abort("500") + } i.Data["json"] = assetIssueList } i.ServeJSON() diff --git a/controllers/numbermessage.go b/controllers/numbermessage.go index cf07fe9d..b7689fe0 100644 --- a/controllers/numbermessage.go +++ b/controllers/numbermessage.go @@ -16,7 +16,10 @@ type NumberMessageController struct { // @Success 200 {nextmaintenancetime} models.NumberMessage // @router /next-maintenance-time [get] func (n *NumberMessageController) NextMaintenanceTime() { - nextMaintenanceTime := models.GetNextMaintenanceTime() + nextMaintenanceTime, err := models.GetNextMaintenanceTime() + if err != nil { + n.Abort("500") + } n.Data["json"] = nextMaintenanceTime n.ServeJSON() } diff --git a/models/account.go b/models/account.go index 7c7b9dc9..6ba48219 100644 --- a/models/account.go +++ b/models/account.go @@ -4,6 +4,7 @@ import ( "github.com/fbsobreira/gotron/common/base58" "github.com/fbsobreira/gotron/common/global" "github.com/fbsobreira/gotron/common/hexutil" + "github.com/fbsobreira/gotron/core" ) type Account struct { @@ -60,10 +61,35 @@ type AccountResource struct { LatestExchangeStorageTime int64 } -func GetAccountByAddress(address string) (*Account, error) { - grpcAccount := global.TronClient.GetAccount(address) +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) + var err error + + grpcContract.OwnerAddress, err = base58.DecodeCheck(contract.OwnerAddress) + if err != nil { + return nil, err + } + grpcContract.AccountAddress, err = base58.DecodeCheck(contract.AccountAddress) + if err != nil { + return nil, err + } + return global.TronClient.CreateAccountByContract(grpcContract) +} +func GetAccountByAddress(address string) (*Account, error) { resultAccount := new(Account) + grpcAccount, err := global.TronClient.GetAccount(address) + if err != nil { + return resultAccount, err + } resultAccount.AccountName = string(grpcAccount.AccountName) resultAccount.AccountType = grpcAccount.Type.String() diff --git a/models/accountnetmessage.go b/models/accountnetmessage.go index 8139145a..628a91cb 100644 --- a/models/accountnetmessage.go +++ b/models/accountnetmessage.go @@ -13,13 +13,12 @@ type AccountNetMessage struct { TotalNetWeight int64 } -func GetAccountNet(address string) AccountNetMessage { - grpcAccountNet := global.TronClient.GetAccountNet(address) - +func GetAccountNet(address string) (AccountNetMessage, error) { var resultAccountNet AccountNetMessage - if grpcAccountNet == nil { - return resultAccountNet + grpcAccountNet, err := global.TronClient.GetAccountNet(address) + if grpcAccountNet == nil || err != nil { + return resultAccountNet, err } resultAccountNet.FreeNetUsed = grpcAccountNet.FreeNetUsed @@ -31,5 +30,5 @@ func GetAccountNet(address string) AccountNetMessage { resultAccountNet.TotalNetLimit = grpcAccountNet.TotalNetLimit resultAccountNet.TotalNetWeight = grpcAccountNet.TotalNetWeight - return resultAccountNet + return resultAccountNet, nil } diff --git a/models/assetissue.go b/models/assetissue.go index 66bea1fd..fdfa2f05 100644 --- a/models/assetissue.go +++ b/models/assetissue.go @@ -33,19 +33,25 @@ type FrozenSupply struct { FrozenDays int64 } -func GetAssetIssueAccount(address string) AssetIssueList { - grpcAssetIssueList := global.TronClient.GetAssetIssueByAccount(address) - +func GetAssetIssueAccount(address string) (AssetIssueList, error) { var resultAssetIssueList AssetIssueList - if grpcAssetIssueList == nil { - return resultAssetIssueList + grpcAssetIssueList, err := global.TronClient.GetAssetIssueByAccount(address) + if err != nil { + return resultAssetIssueList, err + } + + if grpcAssetIssueList == nil || err != nil { + return resultAssetIssueList, err } resultAssetIssueList.AssetIssue = make([]AssetIssueContract, 0) for _, a := range grpcAssetIssueList.AssetIssue { var assetIssueContract AssetIssueContract assetIssueContract.OwnerAddress = base58.EncodeCheck(a.OwnerAddress) + if err != nil { + return resultAssetIssueList, err + } assetIssueContract.Name = string(a.Name) assetIssueContract.Abbr = string(a.Abbr) assetIssueContract.TotalSupply = a.TotalSupply @@ -75,7 +81,7 @@ func GetAssetIssueAccount(address string) AssetIssueList { AssetIssue, assetIssueContract) } - return resultAssetIssueList + return resultAssetIssueList, nil } func GetAssetIssueByName(name string) AssetIssueContract { 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/numbermessage.go b/models/numbermessage.go index 3f6b13aa..40e19586 100644 --- a/models/numbermessage.go +++ b/models/numbermessage.go @@ -6,13 +6,15 @@ type NumberMessage struct { Num int64 } -func GetNextMaintenanceTime() NumberMessage { - grpcNextMaintenanceTime := global.TronClient.GetNextMaintenanceTime() - +func GetNextMaintenanceTime() (NumberMessage, error) { var resultNextMaintenanceTime NumberMessage - resultNextMaintenanceTime.Num = grpcNextMaintenanceTime.Num - return resultNextMaintenanceTime + grpcNextMaintenanceTime, err := global.TronClient.GetNextMaintenanceTime() + if err != nil { + return resultNextMaintenanceTime, err + } + resultNextMaintenanceTime.Num = grpcNextMaintenanceTime.Num + return resultNextMaintenanceTime, nil } func GetTotalTransaction() NumberMessage { diff --git a/service/client.go b/service/client.go index c1445798..e6262012 100644 --- a/service/client.go +++ b/service/client.go @@ -3,6 +3,7 @@ package service import ( "context" "crypto/ecdsa" + "fmt" "log" "strconv" "time" @@ -66,21 +67,19 @@ func (g *GrpcClient) ListNodes() *api.NodeList { return nodeList } -func (g *GrpcClient) GetAccount(address string) *core.Account { +func (g *GrpcClient) GetAccount(address string) (*core.Account, error) { account := new(core.Account) + var err error - account.Address = base58.DecodeCheck(address) + account.Address, err = base58.DecodeCheck(address) + if err != nil { + return nil, err + } 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 + return g.Client.GetAccount(ctx, account) } func (g *GrpcClient) GetNowBlock() *core.Block { @@ -96,35 +95,27 @@ func (g *GrpcClient) GetNowBlock() *core.Block { return result } -func (g *GrpcClient) GetAssetIssueByAccount(address string) *api.AssetIssueList { +func (g *GrpcClient) GetAssetIssueByAccount(address string) (*api.AssetIssueList, error) { account := new(core.Account) + var err error - account.Address = base58.DecodeCheck(address) + account.Address, err = base58.DecodeCheck(address) + if err != nil { + return nil, err + } 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 + return g.Client.GetAssetIssueByAccount(ctx, account) } -func (g *GrpcClient) GetNextMaintenanceTime() *api.NumberMessage { +func (g *GrpcClient) GetNextMaintenanceTime() (*api.NumberMessage, error) { ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) defer cancel() - result, err := g.Client.GetNextMaintenanceTime(ctx, + return 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 { @@ -141,21 +132,19 @@ func (g *GrpcClient) TotalTransaction() *api.NumberMessage { return result } -func (g *GrpcClient) GetAccountNet(address string) *api.AccountNetMessage { +func (g *GrpcClient) GetAccountNet(address string) (*api.AccountNetMessage, error) { account := new(core.Account) + var err error - account.Address = base58.DecodeCheck(address) + account.Address, err = base58.DecodeCheck(address) + if err != nil { + return nil, err + } 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 + return g.Client.GetAccountNet(ctx, account) } func (g *GrpcClient) GetAssetIssueByName(name string) *core.AssetIssueContract { @@ -282,11 +271,15 @@ func (g *GrpcClient) GetBlockByLatestNum(num int64) *api.BlockList { } func (g *GrpcClient) CreateAccount(ownerKey *ecdsa.PrivateKey, - accountAddress string) *api.Return { + accountAddress string) (*api.Return, error) { + var err error accountCreateContract := new(core.AccountCreateContract) accountCreateContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey.PublicKey).Bytes() - accountCreateContract.AccountAddress = base58.DecodeCheck(accountAddress) + accountCreateContract.AccountAddress, err = base58.DecodeCheck(accountAddress) + if err != nil { + return nil, err + } ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) defer cancel() @@ -295,24 +288,18 @@ func (g *GrpcClient) CreateAccount(ownerKey *ecdsa.PrivateKey, accountCreateContract) if err != nil { - log.Fatalf("create account error: %v", err) + return nil, err } if accountCreateTransaction == nil || len(accountCreateTransaction. GetRawData().GetContract()) == 0 { - log.Fatalf("create account error: invalid transaction") + return nil, fmt.Errorf("create account error: invalid transaction") } util.SignTransaction(accountCreateTransaction, ownerKey) - result, err := g.Client.BroadcastTransaction(ctx, + return g.Client.BroadcastTransaction(ctx, accountCreateTransaction) - - if err != nil { - log.Fatalf("create account error: %v", err) - } - - return result } func (g *GrpcClient) CreateAccountByContract(accountCreateContract *core. @@ -360,12 +347,17 @@ func (g *GrpcClient) UpdateAccount(ownerKey *ecdsa.PrivateKey, } func (g *GrpcClient) Transfer(ownerKey *ecdsa.PrivateKey, toAddress string, - amount int64) *api.Return { + amount int64) (*api.Return, error) { + var err error transferContract := new(core.TransferContract) transferContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey. PublicKey).Bytes() - transferContract.ToAddress = base58.DecodeCheck(toAddress) + + transferContract.ToAddress, err = base58.DecodeCheck(toAddress) + if err != nil { + return nil, err + } transferContract.Amount = amount ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) @@ -375,28 +367,23 @@ func (g *GrpcClient) Transfer(ownerKey *ecdsa.PrivateKey, toAddress string, transferContract) if err != nil { - log.Fatalf("transfer error: %v", err) + return nil, err } if transferTransaction == nil || len(transferTransaction. GetRawData().GetContract()) == 0 { - log.Fatalf("transfer error: invalid transaction") + return nil, fmt.Errorf("transfer error: invalid transaction") } util.SignTransaction(transferTransaction, ownerKey) - result, err := g.Client.BroadcastTransaction(ctx, + return 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 { + frozenBalance, frozenDuration int64) (*api.Return, error) { + var err error freezeBalanceContract := new(core.FreezeBalanceContract) freezeBalanceContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey. PublicKey).Bytes() @@ -408,29 +395,25 @@ func (g *GrpcClient) FreezeBalance(ownerKey *ecdsa.PrivateKey, freezeBalanceTransaction, err := g.Client.FreezeBalance(ctx, freezeBalanceContract) - if err != nil { - log.Fatalf("freeze balance error: %v", err) + return nil, err } if freezeBalanceTransaction == nil || len(freezeBalanceTransaction. GetRawData().GetContract()) == 0 { - log.Fatalf("freeze balance error: invalid transaction") + return nil, fmt.Errorf("freeze balance error: invalid transaction") } util.SignTransaction(freezeBalanceTransaction, ownerKey) - result, err := g.Client.BroadcastTransaction(ctx, + return 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 { +func (g *GrpcClient) UnfreezeBalance(ownerKey *ecdsa.PrivateKey) (*api.Return, error) { + var err error + unfreezeBalanceContract := new(core.UnfreezeBalanceContract) unfreezeBalanceContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey.PublicKey).Bytes() @@ -439,26 +422,20 @@ func (g *GrpcClient) UnfreezeBalance(ownerKey *ecdsa.PrivateKey) *api.Return { unfreezeBalanceTransaction, err := g.Client.UnfreezeBalance(ctx, unfreezeBalanceContract) - if err != nil { - log.Fatalf("unfreeze balance error: %v", err) + return nil, err } if unfreezeBalanceTransaction == nil || len(unfreezeBalanceTransaction. GetRawData().GetContract()) == 0 { - log.Fatalf("unfreeze balance error: invalid transaction") + return nil, fmt.Errorf("unfreeze balance error: invalid transaction") } util.SignTransaction(unfreezeBalanceTransaction, ownerKey) - result, err := g.Client.BroadcastTransaction(ctx, + return g.Client.BroadcastTransaction(ctx, unfreezeBalanceTransaction) - if err != nil { - log.Fatalf("unfreeze balance error: %v", err) - } - - return result } func (g *GrpcClient) CreateAssetIssue(ownerKey *ecdsa.PrivateKey, @@ -601,12 +578,17 @@ func (g *GrpcClient) UpdateAssetIssue(ownerKey *ecdsa.PrivateKey, } func (g *GrpcClient) TransferAsset(ownerKey *ecdsa.PrivateKey, toAddress, - assetName string, amount int64) *api.Return { + assetName string, amount int64) (*api.Return, error) { + var err error transferAssetContract := new(core.TransferAssetContract) transferAssetContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey. PublicKey).Bytes() - transferAssetContract.ToAddress = base58.DecodeCheck(toAddress) + + transferAssetContract.ToAddress, err = base58.DecodeCheck(toAddress) + if err != nil { + return nil, err + } transferAssetContract.AssetName = []byte(assetName) transferAssetContract.Amount = amount @@ -615,9 +597,8 @@ func (g *GrpcClient) TransferAsset(ownerKey *ecdsa.PrivateKey, toAddress, transferAssetTransaction, err := g.Client.TransferAsset(ctx, transferAssetContract) - if err != nil { - log.Fatalf("transfer asset error: %v", err) + return nil, err } if transferAssetTransaction == nil || len(transferAssetTransaction. @@ -627,24 +608,23 @@ func (g *GrpcClient) TransferAsset(ownerKey *ecdsa.PrivateKey, toAddress, util.SignTransaction(transferAssetTransaction, ownerKey) - result, err := g.Client.BroadcastTransaction(ctx, + return 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 { + assetName string, amount int64) (*api.Return, error) { + var err error participateAssetIssueContract := new(core.ParticipateAssetIssueContract) participateAssetIssueContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey. PublicKey).Bytes() - participateAssetIssueContract.ToAddress = base58.DecodeCheck(toAddress) + participateAssetIssueContract.ToAddress, err = base58.DecodeCheck(toAddress) + if err != nil { + return nil, err + } participateAssetIssueContract.AssetName = []byte(assetName) participateAssetIssueContract.Amount = amount @@ -653,12 +633,12 @@ func (g *GrpcClient) ParticipateAssetIssue(ownerKey *ecdsa.PrivateKey, Background(), participateAssetIssueContract) if err != nil { - log.Fatalf("participate asset error: %v", err) + return nil, err } if participateAssetIssueTransaction == nil || len(participateAssetIssueTransaction. GetRawData().GetContract()) == 0 { - log.Fatalf("participate asset error: invalid transaction") + return nil, fmt.Errorf("participate asset error: invalid transaction") } util.SignTransaction(participateAssetIssueTransaction, ownerKey) @@ -666,14 +646,8 @@ func (g *GrpcClient) ParticipateAssetIssue(ownerKey *ecdsa.PrivateKey, ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) defer cancel() - result, err := g.Client.BroadcastTransaction(ctx, + return g.Client.BroadcastTransaction(ctx, participateAssetIssueTransaction) - - if err != nil { - log.Fatalf("participate asset error: %v", err) - } - - return result } func (g *GrpcClient) CreateWitness(ownerKey *ecdsa.PrivateKey, @@ -747,18 +721,20 @@ func (g *GrpcClient) UpdateWitness(ownerKey *ecdsa.PrivateKey, } func (g *GrpcClient) VoteWitnessAccount(ownerKey *ecdsa.PrivateKey, - witnessMap map[string]string) *api.Return { + witnessMap map[string]string) (*api.Return, error) { voteWitnessContract := new(core.VoteWitnessContract) voteWitnessContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey. PublicKey).Bytes() for key, value := range witnessMap { - witnessAddress := base58.DecodeCheck(key) + witnessAddress, err := base58.DecodeCheck(key) + if err != nil { + return nil, err + } voteCount, err := strconv.ParseInt(value, 64, 10) - if err != nil { - log.Fatalf("vote witness account error: %v", err) + return nil, err } vote := new(core.VoteWitnessContract_Vote) @@ -774,24 +750,18 @@ func (g *GrpcClient) VoteWitnessAccount(ownerKey *ecdsa.PrivateKey, voteWitnessContract) if err != nil { - log.Fatalf("vote witness account error: %v", err) + return nil, err } if voteWitnessTransaction == nil || len(voteWitnessTransaction. GetRawData().GetContract()) == 0 { - log.Fatalf("vote witness account error: invalid transaction") + return nil, fmt.Errorf("vote witness account error: invalid transaction") } util.SignTransaction(voteWitnessTransaction, ownerKey) - result, err := g.Client.BroadcastTransaction(ctx, + return 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, From a9cf260a6cfa60234206855bbf81ab8f6ff304a7 Mon Sep 17 00:00:00 2001 From: Fernando Sobreira Date: Sun, 3 May 2020 08:15:59 -0300 Subject: [PATCH 2/8] Use go modules --- .DS_Store | Bin 6148 -> 0 bytes .gitignore | 10 +- .gitmodules | 1 + gen-proto => gen-proto.sh | 1 + glide.yaml | 13 - go.mod | 24 ++ go.sum | 622 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 653 insertions(+), 18 deletions(-) delete mode 100644 .DS_Store rename gen-proto => gen-proto.sh (97%) mode change 100755 => 100644 delete mode 100644 glide.yaml create mode 100644 go.mod create mode 100644 go.sum diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 Date: Sun, 3 May 2020 08:16:16 -0300 Subject: [PATCH 3/8] Update REDME --- README.md | 75 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 28 deletions(-) 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 ``` From f356c558c962185d71f23e827779f424bdfb655b Mon Sep 17 00:00:00 2001 From: Fernando Sobreira Date: Sun, 3 May 2020 08:22:20 -0300 Subject: [PATCH 4/8] Refactor structure --- cmd/main.go | 39 + common/base58/base58_test.go | 23 - common/crypto/crypto.go | 153 ---- common/crypto/crypto_test.go | 46 - common/crypto/signature.go | 14 - common/global/global.go | 5 - common/hexutil/hexutils_test.go | 76 -- conf/app.conf | 8 - controllers/account.go | 74 -- controllers/assetissue.go | 55 -- controllers/block.go | 92 -- controllers/node.go | 22 - controllers/numbermessage.go | 35 - controllers/object.go | 91 -- controllers/transaction.go | 26 - controllers/user.go | 118 --- controllers/witness.go | 22 - e2e/keystore_test.go | 11 + main.go | 23 - models/account.go | 205 ----- models/accountnetmessage.go | 34 - models/assetissue.go | 168 ---- models/block.go | 579 ------------ models/node.go | 45 - models/numbermessage.go | 27 - models/object.go | 52 -- models/transaction.go | 41 - models/user.go | 86 -- models/witness.go | 50 -- pkg/account/creation.go | 48 + pkg/account/export.go | 45 + pkg/account/import.go | 132 +++ pkg/account/removal.go | 27 + pkg/address/address.go | 83 ++ pkg/client/account.go | 119 +++ pkg/client/assets.go | 257 ++++++ pkg/client/bank.go | 81 ++ pkg/client/block.go | 85 ++ pkg/client/client.go | 37 + pkg/client/network.go | 90 ++ pkg/client/transaction/controller.go | 213 +++++ pkg/client/transaction/signer.go | 8 + pkg/client/transfer.go | 40 + pkg/client/witnesses.go | 115 +++ {common/base58 => pkg/common}/base58.go | 8 +- {common/hexutil => pkg/common}/hexutils.go | 10 +- pkg/common/numeric/numeric.go | 666 ++++++++++++++ pkg/common/presentation.go | 27 + pkg/common/values.go | 40 + pkg/keys/encoding.go | 17 + pkg/keys/keys.go | 52 ++ pkg/keys/mnemonic.go | 22 + pkg/keystore/account.go | 160 ++++ pkg/keystore/account_cache.go | 303 +++++++ pkg/keystore/crypto.go | 54 ++ pkg/keystore/errors.go | 68 ++ pkg/keystore/file_cache.go | 102 +++ pkg/keystore/key.go | 235 +++++ pkg/keystore/keystore.go | 483 ++++++++++ pkg/keystore/passphrase.go | 356 ++++++++ pkg/keystore/plain.go | 61 ++ pkg/keystore/store.go | 6 + pkg/keystore/url.go | 104 +++ pkg/keystore/wallet.go | 139 +++ pkg/keystore/watch.go | 108 +++ pkg/keystore/watch_fallback.go | 28 + pkg/ledger/hw_wallet.go | 89 ++ pkg/ledger/nano_S_driver.go | 262 ++++++ pkg/mnemonic/mnemonic.go | 19 + {api => pkg/proto/api}/api.pb.go | 5 +- {api => pkg/proto/api}/zksnark.pb.go | 5 +- {core => pkg/proto/core}/Discover.pb.go | 0 {core => pkg/proto/core}/Tron.pb.go | 3 +- .../proto/core}/TronInventoryItems.pb.go | 0 .../proto/core}/account_contract.pb.go | 0 .../proto/core}/asset_issue_contract.pb.go | 0 .../proto/core}/balance_contract.pb.go | 0 {core => pkg/proto/core}/common.pb.go | 0 .../proto/core}/exchange_contract.pb.go | 0 .../proto/core}/proposal_contract.pb.go | 0 .../proto/core}/shield_contract.pb.go | 0 {core => pkg/proto/core}/smart_contract.pb.go | 0 .../proto/core}/storage_contract.pb.go | 0 .../proto/core}/vote_asset_contract.pb.go | 0 .../proto/core}/witness_contract.pb.go | 0 pkg/store/local.go | 128 +++ program/createaccount.go | 44 - program/createassetissue.go | 116 --- program/freezebalance.go | 47 - program/getaccountbyaddress.go | 34 - program/getaccountnet.go | 32 - program/getassetissuebyaccount.go | 33 - program/getassetissuebyname.go | 32 - program/getassetissuelist.go | 28 - program/getblockbyid.go | 33 - program/getblockbylatestnum.go | 32 - program/getblockbylimitnext.go | 36 - program/getblockbynum.go | 32 - program/getnextmaintenancetime.go | 28 - program/getnowblock.go | 33 - program/gettransactionbyid.go | 33 - program/listnodes.go | 32 - program/listwitnesses.go | 42 - program/totaltransaction.go | 28 - program/transfer.go | 48 - program/transferAsset.go | 52 -- program/unfreezebalance.go | 41 - program/updateaccount.go | 44 - program/updateassetissue.go | 68 -- protocol_util/completeTx.pb.go | 110 --- protocol_util/completeTx.proto | 15 - ...b_com_sasaxie_go-client-api_controllers.go | 234 ----- routers/router.go | 51 -- service/client.go | 833 ------------------ util/block.go | 23 - util/transaction.go | 37 - util/util/completeTx.pb.go | 110 --- 117 files changed, 4975 insertions(+), 4546 deletions(-) create mode 100644 cmd/main.go delete mode 100644 common/base58/base58_test.go delete mode 100644 common/crypto/crypto.go delete mode 100644 common/crypto/crypto_test.go delete mode 100644 common/crypto/signature.go delete mode 100644 common/global/global.go delete mode 100644 common/hexutil/hexutils_test.go delete mode 100644 conf/app.conf delete mode 100644 controllers/account.go delete mode 100644 controllers/assetissue.go delete mode 100644 controllers/block.go delete mode 100644 controllers/node.go delete mode 100644 controllers/numbermessage.go delete mode 100644 controllers/object.go delete mode 100644 controllers/transaction.go delete mode 100644 controllers/user.go delete mode 100644 controllers/witness.go create mode 100644 e2e/keystore_test.go delete mode 100644 main.go delete mode 100644 models/account.go delete mode 100644 models/accountnetmessage.go delete mode 100644 models/assetissue.go delete mode 100644 models/block.go delete mode 100644 models/node.go delete mode 100644 models/numbermessage.go delete mode 100644 models/object.go delete mode 100644 models/transaction.go delete mode 100644 models/user.go delete mode 100644 models/witness.go create mode 100644 pkg/account/creation.go create mode 100644 pkg/account/export.go create mode 100644 pkg/account/import.go create mode 100644 pkg/account/removal.go create mode 100644 pkg/address/address.go create mode 100644 pkg/client/account.go create mode 100644 pkg/client/assets.go create mode 100644 pkg/client/bank.go create mode 100644 pkg/client/block.go create mode 100644 pkg/client/client.go create mode 100644 pkg/client/network.go create mode 100644 pkg/client/transaction/controller.go create mode 100644 pkg/client/transaction/signer.go create mode 100644 pkg/client/transfer.go create mode 100644 pkg/client/witnesses.go rename {common/base58 => pkg/common}/base58.go (84%) rename {common/hexutil => pkg/common}/hexutils.go (93%) create mode 100644 pkg/common/numeric/numeric.go create mode 100644 pkg/common/presentation.go create mode 100644 pkg/common/values.go create mode 100644 pkg/keys/encoding.go create mode 100644 pkg/keys/keys.go create mode 100644 pkg/keys/mnemonic.go create mode 100644 pkg/keystore/account.go create mode 100644 pkg/keystore/account_cache.go create mode 100644 pkg/keystore/crypto.go create mode 100644 pkg/keystore/errors.go create mode 100644 pkg/keystore/file_cache.go create mode 100644 pkg/keystore/key.go create mode 100644 pkg/keystore/keystore.go create mode 100644 pkg/keystore/passphrase.go create mode 100644 pkg/keystore/plain.go create mode 100644 pkg/keystore/store.go create mode 100644 pkg/keystore/url.go create mode 100644 pkg/keystore/wallet.go create mode 100644 pkg/keystore/watch.go create mode 100644 pkg/keystore/watch_fallback.go create mode 100644 pkg/ledger/hw_wallet.go create mode 100644 pkg/ledger/nano_S_driver.go create mode 100644 pkg/mnemonic/mnemonic.go rename {api => pkg/proto/api}/api.pb.go (99%) rename {api => pkg/proto/api}/zksnark.pb.go (99%) rename {core => pkg/proto/core}/Discover.pb.go (100%) rename {core => pkg/proto/core}/Tron.pb.go (99%) rename {core => pkg/proto/core}/TronInventoryItems.pb.go (100%) rename {core => pkg/proto/core}/account_contract.pb.go (100%) rename {core => pkg/proto/core}/asset_issue_contract.pb.go (100%) rename {core => pkg/proto/core}/balance_contract.pb.go (100%) rename {core => pkg/proto/core}/common.pb.go (100%) rename {core => pkg/proto/core}/exchange_contract.pb.go (100%) rename {core => pkg/proto/core}/proposal_contract.pb.go (100%) rename {core => pkg/proto/core}/shield_contract.pb.go (100%) rename {core => pkg/proto/core}/smart_contract.pb.go (100%) rename {core => pkg/proto/core}/storage_contract.pb.go (100%) rename {core => pkg/proto/core}/vote_asset_contract.pb.go (100%) rename {core => pkg/proto/core}/witness_contract.pb.go (100%) create mode 100644 pkg/store/local.go delete mode 100644 program/createaccount.go delete mode 100644 program/createassetissue.go delete mode 100644 program/freezebalance.go delete mode 100644 program/getaccountbyaddress.go delete mode 100644 program/getaccountnet.go delete mode 100644 program/getassetissuebyaccount.go delete mode 100644 program/getassetissuebyname.go delete mode 100644 program/getassetissuelist.go delete mode 100644 program/getblockbyid.go delete mode 100644 program/getblockbylatestnum.go delete mode 100644 program/getblockbylimitnext.go delete mode 100644 program/getblockbynum.go delete mode 100644 program/getnextmaintenancetime.go delete mode 100644 program/getnowblock.go delete mode 100644 program/gettransactionbyid.go delete mode 100644 program/listnodes.go delete mode 100644 program/listwitnesses.go delete mode 100644 program/totaltransaction.go delete mode 100644 program/transfer.go delete mode 100644 program/transferAsset.go delete mode 100644 program/unfreezebalance.go delete mode 100644 program/updateaccount.go delete mode 100644 program/updateassetissue.go delete mode 100644 protocol_util/completeTx.pb.go delete mode 100644 protocol_util/completeTx.proto delete mode 100644 routers/commentsRouter___________________Users_xiexiaodong_go_src_github_com_sasaxie_go-client-api_controllers.go delete mode 100644 routers/router.go delete mode 100644 service/client.go delete mode 100644 util/block.go delete mode 100644 util/transaction.go delete mode 100644 util/util/completeTx.pb.go diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 00000000..47e80035 --- /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/common/base58/base58_test.go b/common/base58/base58_test.go deleted file mode 100644 index a191baaf..00000000 --- a/common/base58/base58_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package base58 - -import ( - "strings" - "testing" - - "github.com/fbsobreira/gotron/common/hexutil" -) - -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 edf644b4..00000000 --- a/controllers/account.go +++ /dev/null @@ -1,74 +0,0 @@ -package controllers - -import ( - "github.com/fbsobreira/gotron/models" - - "encoding/json" - - "github.com/astaxie/beego" -) - -// 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, err := models.GetAccountNet(address) - if err != nil { - a.Abort("500") - } - 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 models.AccountCreateContract - err := json.Unmarshal(a.Ctx.Input.RequestBody, &accountCreateContract) - - if err != nil { - a.Data["json"] = err.Error() - } else { - transaction, err := models.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 32243bb8..00000000 --- a/controllers/assetissue.go +++ /dev/null @@ -1,55 +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, err := models.GetAssetIssueAccount(address) - if err != nil { - i.Abort("500") - } - 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 b7689fe0..00000000 --- a/controllers/numbermessage.go +++ /dev/null @@ -1,35 +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, err := models.GetNextMaintenanceTime() - if err != nil { - n.Abort("500") - } - 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/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 6ba48219..00000000 --- a/models/account.go +++ /dev/null @@ -1,205 +0,0 @@ -package models - -import ( - "github.com/fbsobreira/gotron/common/base58" - "github.com/fbsobreira/gotron/common/global" - "github.com/fbsobreira/gotron/common/hexutil" - "github.com/fbsobreira/gotron/core" -) - -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 -} - -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) - var err error - - grpcContract.OwnerAddress, err = base58.DecodeCheck(contract.OwnerAddress) - if err != nil { - return nil, err - } - grpcContract.AccountAddress, err = base58.DecodeCheck(contract.AccountAddress) - if err != nil { - return nil, err - } - return global.TronClient.CreateAccountByContract(grpcContract) -} - -func GetAccountByAddress(address string) (*Account, error) { - resultAccount := new(Account) - grpcAccount, err := global.TronClient.GetAccount(address) - if err != nil { - return resultAccount, err - } - - 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 628a91cb..00000000 --- a/models/accountnetmessage.go +++ /dev/null @@ -1,34 +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, error) { - var resultAccountNet AccountNetMessage - - grpcAccountNet, err := global.TronClient.GetAccountNet(address) - if grpcAccountNet == nil || err != nil { - return resultAccountNet, err - } - - 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, nil -} diff --git a/models/assetissue.go b/models/assetissue.go deleted file mode 100644 index fdfa2f05..00000000 --- a/models/assetissue.go +++ /dev/null @@ -1,168 +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, error) { - var resultAssetIssueList AssetIssueList - - grpcAssetIssueList, err := global.TronClient.GetAssetIssueByAccount(address) - if err != nil { - return resultAssetIssueList, err - } - - if grpcAssetIssueList == nil || err != nil { - return resultAssetIssueList, err - } - - resultAssetIssueList.AssetIssue = make([]AssetIssueContract, 0) - for _, a := range grpcAssetIssueList.AssetIssue { - var assetIssueContract AssetIssueContract - assetIssueContract.OwnerAddress = base58.EncodeCheck(a.OwnerAddress) - if err != nil { - return resultAssetIssueList, err - } - 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, nil -} - -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/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 40e19586..00000000 --- a/models/numbermessage.go +++ /dev/null @@ -1,27 +0,0 @@ -package models - -import "github.com/fbsobreira/gotron/common/global" - -type NumberMessage struct { - Num int64 -} - -func GetNextMaintenanceTime() (NumberMessage, error) { - var resultNextMaintenanceTime NumberMessage - - grpcNextMaintenanceTime, err := global.TronClient.GetNextMaintenanceTime() - if err != nil { - return resultNextMaintenanceTime, err - } - resultNextMaintenanceTime.Num = grpcNextMaintenanceTime.Num - return resultNextMaintenanceTime, nil -} - -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..df777848 --- /dev/null +++ b/pkg/account/creation.go @@ -0,0 +1,48 @@ +package account + +import ( + "errors" + + "github.com/fbsobreira/gotron-sdk/pkg/keys" + "github.com/fbsobreira/gotron-sdk/pkg/mnemonic" + "github.com/fbsobreira/gotron-sdk/pkg/store" +) + +var ( + AccountByNameExists = errors.New("name chosen for account already exists") +) + +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..0b91d4e4 --- /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 84% rename from common/base58/base58.go rename to pkg/common/base58.go index 1fb36c12..21973063 100644 --- a/common/base58/base58.go +++ b/pkg/common/base58.go @@ -1,4 +1,4 @@ -package base58 +package common import ( "crypto/sha256" @@ -7,10 +7,8 @@ import ( "github.com/shengdoushi/base58" ) -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 { @@ -29,7 +27,7 @@ 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, 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 e6262012..00000000 --- a/service/client.go +++ /dev/null @@ -1,833 +0,0 @@ -package service - -import ( - "context" - "crypto/ecdsa" - "fmt" - "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, error) { - account := new(core.Account) - var err error - - account.Address, err = base58.DecodeCheck(address) - if err != nil { - return nil, err - } - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - return g.Client.GetAccount(ctx, account) -} - -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, error) { - account := new(core.Account) - var err error - - account.Address, err = base58.DecodeCheck(address) - if err != nil { - return nil, err - } - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - return g.Client.GetAssetIssueByAccount(ctx, account) -} - -func (g *GrpcClient) GetNextMaintenanceTime() (*api.NumberMessage, error) { - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - return g.Client.GetNextMaintenanceTime(ctx, - new(api.EmptyMessage)) -} - -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, error) { - account := new(core.Account) - var err error - - account.Address, err = base58.DecodeCheck(address) - if err != nil { - return nil, err - } - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - return g.Client.GetAccountNet(ctx, account) -} - -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, error) { - var err error - - accountCreateContract := new(core.AccountCreateContract) - accountCreateContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey.PublicKey).Bytes() - accountCreateContract.AccountAddress, err = base58.DecodeCheck(accountAddress) - if err != nil { - return nil, err - } - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - accountCreateTransaction, err := g.Client.CreateAccount(ctx, - accountCreateContract) - - if err != nil { - return nil, err - } - - if accountCreateTransaction == nil || len(accountCreateTransaction. - GetRawData().GetContract()) == 0 { - return nil, fmt.Errorf("create account error: invalid transaction") - } - - util.SignTransaction(accountCreateTransaction, ownerKey) - - return g.Client.BroadcastTransaction(ctx, - accountCreateTransaction) -} - -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, error) { - var err error - - transferContract := new(core.TransferContract) - transferContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey. - PublicKey).Bytes() - - transferContract.ToAddress, err = base58.DecodeCheck(toAddress) - if err != nil { - return nil, err - } - transferContract.Amount = amount - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - transferTransaction, err := g.Client.CreateTransaction(ctx, - transferContract) - - if err != nil { - return nil, err - } - - if transferTransaction == nil || len(transferTransaction. - GetRawData().GetContract()) == 0 { - return nil, fmt.Errorf("transfer error: invalid transaction") - } - - util.SignTransaction(transferTransaction, ownerKey) - - return g.Client.BroadcastTransaction(ctx, - transferTransaction) -} - -func (g *GrpcClient) FreezeBalance(ownerKey *ecdsa.PrivateKey, - frozenBalance, frozenDuration int64) (*api.Return, error) { - var err error - 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 { - return nil, err - } - - if freezeBalanceTransaction == nil || len(freezeBalanceTransaction. - GetRawData().GetContract()) == 0 { - return nil, fmt.Errorf("freeze balance error: invalid transaction") - } - - util.SignTransaction(freezeBalanceTransaction, ownerKey) - - return g.Client.BroadcastTransaction(ctx, - freezeBalanceTransaction) - -} - -func (g *GrpcClient) UnfreezeBalance(ownerKey *ecdsa.PrivateKey) (*api.Return, error) { - var err error - - 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 { - return nil, err - } - - if unfreezeBalanceTransaction == nil || len(unfreezeBalanceTransaction. - GetRawData().GetContract()) == 0 { - return nil, fmt.Errorf("unfreeze balance error: invalid transaction") - } - - util.SignTransaction(unfreezeBalanceTransaction, ownerKey) - - return g.Client.BroadcastTransaction(ctx, - unfreezeBalanceTransaction) - -} - -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, error) { - var err error - - transferAssetContract := new(core.TransferAssetContract) - transferAssetContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey. - PublicKey).Bytes() - - transferAssetContract.ToAddress, err = base58.DecodeCheck(toAddress) - if err != nil { - return nil, err - } - 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 { - return nil, err - } - - if transferAssetTransaction == nil || len(transferAssetTransaction. - GetRawData().GetContract()) == 0 { - log.Fatalf("transfer asset error: invalid transaction") - } - - util.SignTransaction(transferAssetTransaction, ownerKey) - - return g.Client.BroadcastTransaction(ctx, - transferAssetTransaction) - -} - -func (g *GrpcClient) ParticipateAssetIssue(ownerKey *ecdsa.PrivateKey, - toAddress, - assetName string, amount int64) (*api.Return, error) { - var err error - - participateAssetIssueContract := new(core.ParticipateAssetIssueContract) - participateAssetIssueContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey. - PublicKey).Bytes() - participateAssetIssueContract.ToAddress, err = base58.DecodeCheck(toAddress) - if err != nil { - return nil, err - } - participateAssetIssueContract.AssetName = []byte(assetName) - participateAssetIssueContract.Amount = amount - - participateAssetIssueTransaction, err := g.Client.ParticipateAssetIssue( - context. - Background(), participateAssetIssueContract) - - if err != nil { - return nil, err - } - - if participateAssetIssueTransaction == nil || len(participateAssetIssueTransaction. - GetRawData().GetContract()) == 0 { - return nil, fmt.Errorf("participate asset error: invalid transaction") - } - - util.SignTransaction(participateAssetIssueTransaction, ownerKey) - - ctx, cancel := context.WithTimeout(context.Background(), GrpcTimeout) - defer cancel() - - return g.Client.BroadcastTransaction(ctx, - participateAssetIssueTransaction) -} - -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, error) { - - voteWitnessContract := new(core.VoteWitnessContract) - voteWitnessContract.OwnerAddress = crypto.PubkeyToAddress(ownerKey. - PublicKey).Bytes() - - for key, value := range witnessMap { - witnessAddress, err := base58.DecodeCheck(key) - if err != nil { - return nil, err - } - voteCount, err := strconv.ParseInt(value, 64, 10) - if err != nil { - return nil, 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 { - return nil, err - } - - if voteWitnessTransaction == nil || len(voteWitnessTransaction. - GetRawData().GetContract()) == 0 { - return nil, fmt.Errorf("vote witness account error: invalid transaction") - } - - util.SignTransaction(voteWitnessTransaction, ownerKey) - - return g.Client.BroadcastTransaction(ctx, - voteWitnessTransaction) -} - -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, -} From c0d43959ed6d23c142ea8de714086f060065ad82 Mon Sep 17 00:00:00 2001 From: Fernando Sobreira Date: Sun, 3 May 2020 08:22:28 -0300 Subject: [PATCH 5/8] Add commands --- cmd/subcommands/account.go | 108 +++++++++++ cmd/subcommands/assets.go | 55 ++++++ cmd/subcommands/completion.go | 25 +++ cmd/subcommands/custom-flags.go | 35 ++++ cmd/subcommands/keys.go | 326 ++++++++++++++++++++++++++++++++ cmd/subcommands/root.go | 162 ++++++++++++++++ cmd/subcommands/utility.go | 55 ++++++ cmd/subcommands/values.go | 12 ++ 8 files changed, 778 insertions(+) create mode 100644 cmd/subcommands/account.go create mode 100644 cmd/subcommands/assets.go create mode 100644 cmd/subcommands/completion.go create mode 100644 cmd/subcommands/custom-flags.go create mode 100644 cmd/subcommands/keys.go create mode 100644 cmd/subcommands/root.go create mode 100644 cmd/subcommands/utility.go create mode 100644 cmd/subcommands/values.go 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..5e8b478c --- /dev/null +++ b/cmd/subcommands/root.go @@ -0,0 +1,162 @@ +package cmd + +import ( + "bytes" + "fmt" + "net/http" + "os" + "path" + "regexp" + "strings" + + 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 the Tron blockchain + +%s`, g("type 'tronclt --help' 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://cryptochain.network/tronctl_ver" + versionFormat = regexp.MustCompile("v[0-9]+-[a-z0-9]{7}") +) + +// Execute kicks off the tronctl CLI +func Execute() { + RootCmd.SilenceErrors = true + if err := RootCmd.Execute(); err != nil { + 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) + + currentVersion := versionFormat.FindAllString(buf.String(), 1) + if currentVersion != nil && currentVersion[0] != VersionWrapDump { + warnMsg := fmt.Sprintf("Warning: Using outdated version. Redownload to upgrade to %s\n", currentVersion[0]) + fmt.Fprintf(os.Stderr, color.RedString(warnMsg)) + } + } + 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 one 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() +) From 19983f80481023b9c5f6df7254b05ffb2a869747 Mon Sep 17 00:00:00 2001 From: Fernando Sobreira Date: Sun, 3 May 2020 10:28:04 -0300 Subject: [PATCH 6/8] Check github release --- cmd/main.go | 2 +- cmd/subcommands/root.go | 88 +++++++++++++++++++++++++++++++++-------- 2 files changed, 73 insertions(+), 17 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 47e80035..5e3bb5be 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -29,7 +29,7 @@ func main() { Short: "Show version", RunE: func(cmd *cobra.Command, args []string) error { fmt.Fprintf(os.Stderr, - "TronCTL. %v, version %v-%v (%v %v)\n", + "TronCTL. %v version %v-%v (%v %v)\n", path.Base(os.Args[0]), version, commit, builtBy, builtAt) os.Exit(0) return nil diff --git a/cmd/subcommands/root.go b/cmd/subcommands/root.go index 5e8b478c..dfe1145a 100644 --- a/cmd/subcommands/root.go +++ b/cmd/subcommands/root.go @@ -2,12 +2,14 @@ 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" @@ -62,9 +64,9 @@ var ( return nil }, Long: fmt.Sprintf(` -CLI interface to the Tron blockchain +CLI interface to Tron blockchain -%s`, g("type 'tronclt --help' details")), +%s`, g("type 'tronclt --help' for details")), RunE: func(cmd *cobra.Command, args []string) error { cmd.Help() return nil @@ -101,26 +103,80 @@ func init() { var ( // VersionWrapDump meant to be set from main.go VersionWrapDump = "" - versionLink = "https://cryptochain.network/tronctl_ver" + 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 { - 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) - - currentVersion := versionFormat.FindAllString(buf.String(), 1) - if currentVersion != nil && currentVersion[0] != VersionWrapDump { - warnMsg := fmt.Sprintf("Warning: Using outdated version. Redownload to upgrade to %s\n", currentVersion[0]) - fmt.Fprintf(os.Stderr, color.RedString(warnMsg)) - } + if tag, errGit := getGitVersion(); errGit == nil { + VersionWrapDump += ":" + tag } errMsg := errors.Wrapf(err, "commit: %s, error", VersionWrapDump).Error() fmt.Fprintf(os.Stderr, errMsg+"\n") @@ -144,7 +200,7 @@ func findAddress(value string) (tronAddress, error) { if acc, err := store.AddressFromAccountName(value); err == nil { return tronAddress{acc}, nil } - return address, fmt.Errorf("Invalid one address/Invalid account name: %s", value) + return address, fmt.Errorf("Invalid address/Invalid account name: %s", value) } return address, nil } From 3ef1f9822f30715f9963d147d9c2cd4762917f62 Mon Sep 17 00:00:00 2001 From: Fernando Sobreira Date: Sun, 3 May 2020 10:28:16 -0300 Subject: [PATCH 7/8] Makefile --- Makefile | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Makefile 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 From e70f5f63e4bc922b8e8eb42529d59255b57c4612 Mon Sep 17 00:00:00 2001 From: Fernando Sobreira Date: Sun, 3 May 2020 10:38:05 -0300 Subject: [PATCH 8/8] fix error message --- pkg/account/creation.go | 7 +------ pkg/account/import.go | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/pkg/account/creation.go b/pkg/account/creation.go index df777848..e74b7aaf 100644 --- a/pkg/account/creation.go +++ b/pkg/account/creation.go @@ -1,17 +1,12 @@ package account import ( - "errors" - "github.com/fbsobreira/gotron-sdk/pkg/keys" "github.com/fbsobreira/gotron-sdk/pkg/mnemonic" "github.com/fbsobreira/gotron-sdk/pkg/store" ) -var ( - AccountByNameExists = errors.New("name chosen for account already exists") -) - +// Creation struct for account type Creation struct { Name string Passphrase string diff --git a/pkg/account/import.go b/pkg/account/import.go index 0b91d4e4..39168f65 100644 --- a/pkg/account/import.go +++ b/pkg/account/import.go @@ -120,7 +120,7 @@ func ImportKeyStore(keyPath, name, passphrase string) (string, error) { hasAddress := store.FromAddress(key.Address.String()) != nil if hasAddress { - return "", fmt.Errorf("address %s already exists in keystore", key.Address.String) + 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))