diff --git a/cmd/client/wallet/main.go b/cmd/client/wallet/main.go index 150293e38..625511a59 100644 --- a/cmd/client/wallet/main.go +++ b/cmd/client/wallet/main.go @@ -13,6 +13,7 @@ import ( "os" "path" "strconv" + "time" "github.com/ethereum/go-ethereum/common" crypto2 "github.com/ethereum/go-ethereum/crypto" @@ -61,6 +62,13 @@ var ( balanceCommand = flag.NewFlagSet("getFreeToken", flag.ExitOnError) balanceAddressPtr = balanceCommand.String("address", "", "Specify the account address to check balance for") + + // Quit the program once stopChan received message + stopChan = make(chan struct{}) + // Print out progress char + tick = time.NewTicker(time.Second) + // Flag to keep waiting for async call finished + async = true ) // setupLog setup log for verbose output @@ -110,25 +118,36 @@ func main() { case "-version": printVersion(os.Args[0]) case "new": - processNewCommnad() + go processNewCommnad() case "list": - processListCommand() + go processListCommand() case "removeAll": - clearKeystore() + go clearKeystore() fmt.Println("All existing accounts deleted...") case "import": - processImportCommnad() + go processImportCommnad() case "balances": - processBalancesCommand() + go processBalancesCommand() case "getFreeToken": - processGetFreeToken() + go processGetFreeToken() case "transfer": - processTransferCommand() + go processTransferCommand() default: fmt.Printf("Unknown action: %s\n", os.Args[1]) flag.PrintDefaults() os.Exit(1) } + + // Waiting for async call finished and print out some progress + for async { + select { + case <-tick.C: + fmt.Printf("=") + case <-stopChan: + fmt.Println("Done.") + os.Exit(0) + } + } } func processNewCommnad() { @@ -137,6 +156,7 @@ func processNewCommnad() { if err != nil { fmt.Println("Failed to get randomness for the private key...") + async = false return } priKey, err := crypto2.GenerateKey() @@ -146,6 +166,7 @@ func processNewCommnad() { storePrivateKey(crypto2.FromECDSA(priKey)) fmt.Printf("New account created with address:{%s}\n", crypto2.PubkeyToAddress(priKey.PublicKey).Hex()) fmt.Printf("Please keep a copy of the private key:{%s}\n", hex.EncodeToString(crypto2.FromECDSA(priKey))) + async = false } func processListCommand() { @@ -153,6 +174,7 @@ func processListCommand() { fmt.Printf("Account %d:{%s}\n", i, crypto2.PubkeyToAddress(key.PublicKey).Hex()) fmt.Printf(" PrivateKey:{%s}\n", hex.EncodeToString(key.D.Bytes())) } + async = false } func processImportCommnad() { @@ -160,6 +182,7 @@ func processImportCommnad() { priKey := *accountImportPtr if priKey == "" { fmt.Println("Error: --privateKey is required") + async = false return } if !accountImportCommand.Parsed() { @@ -171,6 +194,7 @@ func processImportCommnad() { } storePrivateKey(priKeyBytes) fmt.Println("Private key imported...") + async = false } func processBalancesCommand() { @@ -181,16 +205,17 @@ func processBalancesCommand() { for i, address := range ReadAddresses() { fmt.Printf("Account %d: %s:\n", i, address.Hex()) for shardID, balanceNonce := range FetchBalance(address, walletNode) { - fmt.Printf(" Balance in Shard %d: %s \n", shardID, convertBalanceIntoReadableFormat(balanceNonce.balance)) + fmt.Printf(" Balance in Shard %d: %s, nonce: %v \n", shardID, convertBalanceIntoReadableFormat(balanceNonce.balance), balanceNonce.nonce) } } } else { address := common.HexToAddress(*balanceAddressPtr) fmt.Printf("Account: %s:\n", address.Hex()) for shardID, balanceNonce := range FetchBalance(address, walletNode) { - fmt.Printf(" Balance in Shard %d: %s \n", shardID, convertBalanceIntoReadableFormat(balanceNonce.balance)) + fmt.Printf(" Balance in Shard %d: %s, nonce: %v \n", shardID, convertBalanceIntoReadableFormat(balanceNonce.balance), balanceNonce.nonce) } } + async = false } func processGetFreeToken() { @@ -199,11 +224,14 @@ func processGetFreeToken() { if *freeTokenAddressPtr == "" { fmt.Println("Error: --address is required") + async = false return } address := common.HexToAddress(*freeTokenAddressPtr) GetFreeToken(address, walletNode) + + async = false } func processTransferCommand() { @@ -221,20 +249,24 @@ func processTransferCommand() { if err != nil { fmt.Printf("Cannot base64-decode input data (%s): %s\n", base64InputData, err) + async = false return } if shardID == -1 { fmt.Println("Please specify the shard ID for the transfer (e.g. --shardID=0)") + async = false return } if amount <= 0 { fmt.Println("Please specify positive amount to transfer") + async = false return } priKeys := readPrivateKeys() if len(priKeys) == 0 { fmt.Println("No imported account to use.") + async = false return } senderIndex, err := strconv.Atoi(sender) @@ -249,18 +281,21 @@ func processTransferCommand() { } if senderIndex == -1 { fmt.Println("The specified sender account does not exist in the wallet.") + async = false return } } if senderIndex >= len(priKeys) { fmt.Println("Sender account index out of bounds.") + async = false return } receiverAddress := common.HexToAddress(receiver) if len(receiverAddress) != 20 { fmt.Println("The receiver address is not valid.") + async = false return } @@ -273,19 +308,21 @@ func processTransferCommand() { state, ok := shardIDToAccountState[uint32(shardID)] if !ok { fmt.Printf("Failed connecting to the shard %d\n", shardID) + async = false return } balance := state.balance balance = balance.Div(balance, big.NewInt(params.GWei)) if amount > float64(balance.Uint64())/params.GWei { fmt.Printf("Balance is not enough for the transfer, current balance is %.6f\n", float64(balance.Uint64())/params.GWei) + async = false return } amountBigInt := big.NewInt(int64(amount * params.GWei)) amountBigInt = amountBigInt.Mul(amountBigInt, big.NewInt(params.GWei)) tx, _ := types.SignTx(types.NewTransaction(state.nonce, receiverAddress, uint32(shardID), amountBigInt, params.TxGas, nil, inputData), types.HomesteadSigner{}, senderPriKey) - wallet.SubmitTransaction(tx, walletNode, uint32(shardID)) + wallet.SubmitTransaction(tx, walletNode, uint32(shardID), stopChan) } func convertBalanceIntoReadableFormat(balance *big.Int) string { diff --git a/internal/wallet/wallet/lib.go b/internal/wallet/wallet/lib.go index 9668a93a3..3938ecae0 100644 --- a/internal/wallet/wallet/lib.go +++ b/internal/wallet/wallet/lib.go @@ -34,6 +34,9 @@ func CreateWalletNode() *node.Node { walletNode.NodeConfig.SetRole(nodeconfig.ClientNode) walletNode.ServiceManagerSetup() walletNode.RunServices() + // wait for networkinfo/discovery service to start fully + // FIXME (leo): use async mode or channel to communicate + time.Sleep(2 * time.Second) return walletNode } @@ -42,6 +45,8 @@ func CreateWalletNode() *node.Node { func GetPeersFromBeaconChain(walletNode *node.Node) []p2p.Peer { peers := []p2p.Peer{} + // wait until we got beacon peer + // FIXME (chao): use async channel for communiation time.Sleep(4 * time.Second) walletNode.BeaconNeighbors.Range(func(k, v interface{}) bool { peers = append(peers, v.(p2p.Peer)) @@ -52,16 +57,26 @@ func GetPeersFromBeaconChain(walletNode *node.Node) []p2p.Peer { } // SubmitTransaction submits the transaction to the Harmony network -func SubmitTransaction(tx *types.Transaction, walletNode *node.Node, shardID uint32) error { +func SubmitTransaction(tx *types.Transaction, walletNode *node.Node, shardID uint32, stopChan chan struct{}) error { msg := proto_node.ConstructTransactionListMessageAccount(types.Transactions{tx}) - walletNode.GetHost().SendMessageToGroups([]p2p.GroupID{p2p.GroupIDBeaconClient}, p2p_host.ConstructP2pMessage(byte(0), msg)) + err := walletNode.GetHost().SendMessageToGroups([]p2p.GroupID{p2p.GroupIDBeaconClient}, p2p_host.ConstructP2pMessage(byte(0), msg)) + if err != nil { + fmt.Printf("Error in SubmitTransaction: %v\n", err) + return err + } fmt.Printf("Transaction Id for shard %d: %s\n", int(shardID), tx.Hash().Hex()) + // FIXME (leo): how to we know the tx was successful sent to the network + // this is a hacky way to wait for sometime + time.Sleep(2 * time.Second) + if stopChan != nil { + stopChan <- struct{}{} + } return nil } func getBootNodes() []ma.Multiaddr { - //addrStrings := []string{"/ip4/54.213.43.194/tcp/9874/p2p/QmQhPRqqfTRExqWmTifjMaBvRd3HBmnmj9jYAvTy6HPPJj"} - addrStrings := []string{"/ip4/100.26.90.187/tcp/9871/p2p/QmPH2XsLP88jpfejHycQRWB7vDjwDju9qT9rMmdNaNea5v", "/ip4/54.213.43.194/tcp/9871/p2p/QmQLjTciaJppXVPZFoj4gTdME5axzTxtVwty5Lg8kwt6Zs"} + // These are the bootnodes of banjo testnet + addrStrings := []string{"/ip4/100.26.90.187/tcp/9876/p2p/QmZJJx6AdaoEkGLrYG4JeLCKeCKDjnFz2wfHNHxAqFSGA9", "/ip4/54.213.43.194/tcp/9876/p2p/QmQayinFSgMMw5cSpDUiD9pQ2WeP6WNmGxpZ6ou3mdVFJX"} bootNodeAddrs, err := utils.StringsToAddrs(addrStrings) if err != nil { panic(err) diff --git a/internal/wallet/wallet/lib_test.go b/internal/wallet/wallet/lib_test.go index 7d8ccc1e5..923b9da24 100644 --- a/internal/wallet/wallet/lib_test.go +++ b/internal/wallet/wallet/lib_test.go @@ -41,7 +41,7 @@ func TestSubmitTransaction(test *testing.T) { msg := proto_node.ConstructTransactionListMessageAccount(types.Transactions{tx}) m.EXPECT().SendMessageToGroups([]p2p.GroupID{p2p.GroupIDBeaconClient}, p2p_host.ConstructP2pMessage(byte(0), msg)) - SubmitTransaction(tx, walletNode, 0) + SubmitTransaction(tx, walletNode, 0, nil) time.Sleep(1 * time.Second) }