[wallet] fix transfer not working

The main problem is the wallet is using p2p for async communication and
it exits too fast before p2p finished the tasks.

So, we added a few seconds waiting in the program and it works fine.

Signed-off-by: Leo Chen <leo@harmony.one>
pull/607/head
Leo Chen 6 years ago
parent 531a2a7ccb
commit c01bc303a7
  1. 57
      cmd/client/wallet/main.go
  2. 23
      internal/wallet/wallet/lib.go
  3. 2
      internal/wallet/wallet/lib_test.go

@ -13,6 +13,7 @@ import (
"os" "os"
"path" "path"
"strconv" "strconv"
"time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
crypto2 "github.com/ethereum/go-ethereum/crypto" crypto2 "github.com/ethereum/go-ethereum/crypto"
@ -61,6 +62,13 @@ var (
balanceCommand = flag.NewFlagSet("getFreeToken", flag.ExitOnError) balanceCommand = flag.NewFlagSet("getFreeToken", flag.ExitOnError)
balanceAddressPtr = balanceCommand.String("address", "", "Specify the account address to check balance for") 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 // setupLog setup log for verbose output
@ -110,25 +118,36 @@ func main() {
case "-version": case "-version":
printVersion(os.Args[0]) printVersion(os.Args[0])
case "new": case "new":
processNewCommnad() go processNewCommnad()
case "list": case "list":
processListCommand() go processListCommand()
case "removeAll": case "removeAll":
clearKeystore() go clearKeystore()
fmt.Println("All existing accounts deleted...") fmt.Println("All existing accounts deleted...")
case "import": case "import":
processImportCommnad() go processImportCommnad()
case "balances": case "balances":
processBalancesCommand() go processBalancesCommand()
case "getFreeToken": case "getFreeToken":
processGetFreeToken() go processGetFreeToken()
case "transfer": case "transfer":
processTransferCommand() go processTransferCommand()
default: default:
fmt.Printf("Unknown action: %s\n", os.Args[1]) fmt.Printf("Unknown action: %s\n", os.Args[1])
flag.PrintDefaults() flag.PrintDefaults()
os.Exit(1) 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() { func processNewCommnad() {
@ -137,6 +156,7 @@ func processNewCommnad() {
if err != nil { if err != nil {
fmt.Println("Failed to get randomness for the private key...") fmt.Println("Failed to get randomness for the private key...")
async = false
return return
} }
priKey, err := crypto2.GenerateKey() priKey, err := crypto2.GenerateKey()
@ -146,6 +166,7 @@ func processNewCommnad() {
storePrivateKey(crypto2.FromECDSA(priKey)) storePrivateKey(crypto2.FromECDSA(priKey))
fmt.Printf("New account created with address:{%s}\n", crypto2.PubkeyToAddress(priKey.PublicKey).Hex()) 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))) fmt.Printf("Please keep a copy of the private key:{%s}\n", hex.EncodeToString(crypto2.FromECDSA(priKey)))
async = false
} }
func processListCommand() { func processListCommand() {
@ -153,6 +174,7 @@ func processListCommand() {
fmt.Printf("Account %d:{%s}\n", i, crypto2.PubkeyToAddress(key.PublicKey).Hex()) fmt.Printf("Account %d:{%s}\n", i, crypto2.PubkeyToAddress(key.PublicKey).Hex())
fmt.Printf(" PrivateKey:{%s}\n", hex.EncodeToString(key.D.Bytes())) fmt.Printf(" PrivateKey:{%s}\n", hex.EncodeToString(key.D.Bytes()))
} }
async = false
} }
func processImportCommnad() { func processImportCommnad() {
@ -160,6 +182,7 @@ func processImportCommnad() {
priKey := *accountImportPtr priKey := *accountImportPtr
if priKey == "" { if priKey == "" {
fmt.Println("Error: --privateKey is required") fmt.Println("Error: --privateKey is required")
async = false
return return
} }
if !accountImportCommand.Parsed() { if !accountImportCommand.Parsed() {
@ -171,6 +194,7 @@ func processImportCommnad() {
} }
storePrivateKey(priKeyBytes) storePrivateKey(priKeyBytes)
fmt.Println("Private key imported...") fmt.Println("Private key imported...")
async = false
} }
func processBalancesCommand() { func processBalancesCommand() {
@ -181,16 +205,17 @@ func processBalancesCommand() {
for i, address := range ReadAddresses() { for i, address := range ReadAddresses() {
fmt.Printf("Account %d: %s:\n", i, address.Hex()) fmt.Printf("Account %d: %s:\n", i, address.Hex())
for shardID, balanceNonce := range FetchBalance(address, walletNode) { 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 { } else {
address := common.HexToAddress(*balanceAddressPtr) address := common.HexToAddress(*balanceAddressPtr)
fmt.Printf("Account: %s:\n", address.Hex()) fmt.Printf("Account: %s:\n", address.Hex())
for shardID, balanceNonce := range FetchBalance(address, walletNode) { 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() { func processGetFreeToken() {
@ -199,11 +224,14 @@ func processGetFreeToken() {
if *freeTokenAddressPtr == "" { if *freeTokenAddressPtr == "" {
fmt.Println("Error: --address is required") fmt.Println("Error: --address is required")
async = false
return return
} }
address := common.HexToAddress(*freeTokenAddressPtr) address := common.HexToAddress(*freeTokenAddressPtr)
GetFreeToken(address, walletNode) GetFreeToken(address, walletNode)
async = false
} }
func processTransferCommand() { func processTransferCommand() {
@ -221,20 +249,24 @@ func processTransferCommand() {
if err != nil { if err != nil {
fmt.Printf("Cannot base64-decode input data (%s): %s\n", fmt.Printf("Cannot base64-decode input data (%s): %s\n",
base64InputData, err) base64InputData, err)
async = false
return return
} }
if shardID == -1 { if shardID == -1 {
fmt.Println("Please specify the shard ID for the transfer (e.g. --shardID=0)") fmt.Println("Please specify the shard ID for the transfer (e.g. --shardID=0)")
async = false
return return
} }
if amount <= 0 { if amount <= 0 {
fmt.Println("Please specify positive amount to transfer") fmt.Println("Please specify positive amount to transfer")
async = false
return return
} }
priKeys := readPrivateKeys() priKeys := readPrivateKeys()
if len(priKeys) == 0 { if len(priKeys) == 0 {
fmt.Println("No imported account to use.") fmt.Println("No imported account to use.")
async = false
return return
} }
senderIndex, err := strconv.Atoi(sender) senderIndex, err := strconv.Atoi(sender)
@ -249,18 +281,21 @@ func processTransferCommand() {
} }
if senderIndex == -1 { if senderIndex == -1 {
fmt.Println("The specified sender account does not exist in the wallet.") fmt.Println("The specified sender account does not exist in the wallet.")
async = false
return return
} }
} }
if senderIndex >= len(priKeys) { if senderIndex >= len(priKeys) {
fmt.Println("Sender account index out of bounds.") fmt.Println("Sender account index out of bounds.")
async = false
return return
} }
receiverAddress := common.HexToAddress(receiver) receiverAddress := common.HexToAddress(receiver)
if len(receiverAddress) != 20 { if len(receiverAddress) != 20 {
fmt.Println("The receiver address is not valid.") fmt.Println("The receiver address is not valid.")
async = false
return return
} }
@ -273,19 +308,21 @@ func processTransferCommand() {
state, ok := shardIDToAccountState[uint32(shardID)] state, ok := shardIDToAccountState[uint32(shardID)]
if !ok { if !ok {
fmt.Printf("Failed connecting to the shard %d\n", shardID) fmt.Printf("Failed connecting to the shard %d\n", shardID)
async = false
return return
} }
balance := state.balance balance := state.balance
balance = balance.Div(balance, big.NewInt(params.GWei)) balance = balance.Div(balance, big.NewInt(params.GWei))
if amount > float64(balance.Uint64())/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) fmt.Printf("Balance is not enough for the transfer, current balance is %.6f\n", float64(balance.Uint64())/params.GWei)
async = false
return return
} }
amountBigInt := big.NewInt(int64(amount * params.GWei)) amountBigInt := big.NewInt(int64(amount * params.GWei))
amountBigInt = amountBigInt.Mul(amountBigInt, big.NewInt(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) 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 { func convertBalanceIntoReadableFormat(balance *big.Int) string {

@ -34,6 +34,9 @@ func CreateWalletNode() *node.Node {
walletNode.NodeConfig.SetRole(nodeconfig.ClientNode) walletNode.NodeConfig.SetRole(nodeconfig.ClientNode)
walletNode.ServiceManagerSetup() walletNode.ServiceManagerSetup()
walletNode.RunServices() 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 return walletNode
} }
@ -42,6 +45,8 @@ func CreateWalletNode() *node.Node {
func GetPeersFromBeaconChain(walletNode *node.Node) []p2p.Peer { func GetPeersFromBeaconChain(walletNode *node.Node) []p2p.Peer {
peers := []p2p.Peer{} peers := []p2p.Peer{}
// wait until we got beacon peer
// FIXME (chao): use async channel for communiation
time.Sleep(4 * time.Second) time.Sleep(4 * time.Second)
walletNode.BeaconNeighbors.Range(func(k, v interface{}) bool { walletNode.BeaconNeighbors.Range(func(k, v interface{}) bool {
peers = append(peers, v.(p2p.Peer)) 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 // 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}) 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()) 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 return nil
} }
func getBootNodes() []ma.Multiaddr { func getBootNodes() []ma.Multiaddr {
//addrStrings := []string{"/ip4/54.213.43.194/tcp/9874/p2p/QmQhPRqqfTRExqWmTifjMaBvRd3HBmnmj9jYAvTy6HPPJj"} // These are the bootnodes of banjo testnet
addrStrings := []string{"/ip4/100.26.90.187/tcp/9871/p2p/QmPH2XsLP88jpfejHycQRWB7vDjwDju9qT9rMmdNaNea5v", "/ip4/54.213.43.194/tcp/9871/p2p/QmQLjTciaJppXVPZFoj4gTdME5axzTxtVwty5Lg8kwt6Zs"} 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) bootNodeAddrs, err := utils.StringsToAddrs(addrStrings)
if err != nil { if err != nil {
panic(err) panic(err)

@ -41,7 +41,7 @@ func TestSubmitTransaction(test *testing.T) {
msg := proto_node.ConstructTransactionListMessageAccount(types.Transactions{tx}) msg := proto_node.ConstructTransactionListMessageAccount(types.Transactions{tx})
m.EXPECT().SendMessageToGroups([]p2p.GroupID{p2p.GroupIDBeaconClient}, p2p_host.ConstructP2pMessage(byte(0), msg)) 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) time.Sleep(1 * time.Second)
} }

Loading…
Cancel
Save