Merge pull request #609 from LeoHChen/wallet_cleanup_and_simplify

Wallet cleanup and simplify
pull/606/head
Leo Chen 6 years ago committed by GitHub
commit 0398c43222
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 32
      api/client/service/client.go
  2. 205
      cmd/client/wallet/main.go
  3. 85
      internal/wallet/wallet/lib.go
  4. 47
      internal/wallet/wallet/lib_test.go

@ -3,7 +3,6 @@ package client
import ( import (
"context" "context"
"fmt" "fmt"
"log"
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -20,18 +19,17 @@ type Client struct {
} }
// NewClient setups a Client given ip and port. // NewClient setups a Client given ip and port.
func NewClient(ip, port string) *Client { func NewClient(ip, port string) (*Client, error) {
client := Client{} client := Client{}
client.opts = append(client.opts, grpc.WithInsecure()) client.opts = append(client.opts, grpc.WithInsecure())
var err error var err error
client.conn, err = grpc.Dial(fmt.Sprintf("%s:%s", ip, port), client.opts...) client.conn, err = grpc.Dial(fmt.Sprintf("%s:%s", ip, port), client.opts...)
if err != nil { if err != nil {
log.Fatalf("fail to dial: %v", err) return nil, err
return nil
} }
client.clientServiceClient = proto.NewClientServiceClient(client.conn) client.clientServiceClient = proto.NewClientServiceClient(client.conn)
return &client return &client, nil
} }
// Close closes the Client. // Close closes the Client.
@ -40,37 +38,25 @@ func (client *Client) Close() {
} }
// GetBalance gets account balance from the client service. // GetBalance gets account balance from the client service.
func (client *Client) GetBalance(address common.Address) *proto.FetchAccountStateResponse { func (client *Client) GetBalance(address common.Address) (*proto.FetchAccountStateResponse, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
request := &proto.FetchAccountStateRequest{Address: address.Bytes()} request := &proto.FetchAccountStateRequest{Address: address.Bytes()}
response, err := client.clientServiceClient.FetchAccountState(ctx, request) return client.clientServiceClient.FetchAccountState(ctx, request)
if err != nil {
log.Fatalf("Error getting balance: %s", err)
}
return response
} }
// GetFreeToken requests free token from the faucet contract. // GetFreeToken requests free token from the faucet contract.
func (client *Client) GetFreeToken(address common.Address) *proto.GetFreeTokenResponse { func (client *Client) GetFreeToken(address common.Address) (*proto.GetFreeTokenResponse, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
request := &proto.GetFreeTokenRequest{Address: address.Bytes()} request := &proto.GetFreeTokenRequest{Address: address.Bytes()}
response, err := client.clientServiceClient.GetFreeToken(ctx, request) return client.clientServiceClient.GetFreeToken(ctx, request)
if err != nil {
log.Fatalf("Error getting free token: %s", err)
}
return response
} }
// GetStakingContractInfo gets necessary info for staking. // GetStakingContractInfo gets necessary info for staking.
func (client *Client) GetStakingContractInfo(address common.Address) *proto.StakingContractInfoResponse { func (client *Client) GetStakingContractInfo(address common.Address) (*proto.StakingContractInfoResponse, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
request := &proto.StakingContractInfoRequest{Address: address.Bytes()} request := &proto.StakingContractInfoRequest{Address: address.Bytes()}
response, err := client.clientServiceClient.GetStakingContractInfo(ctx, request) return client.clientServiceClient.GetStakingContractInfo(ctx, request)
if err != nil {
log.Fatalf("Error getting free token: %s", err)
}
return response
} }

@ -2,7 +2,7 @@ package main
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
"crypto/rand" cRand "crypto/rand"
"encoding/base64" "encoding/base64"
"encoding/hex" "encoding/hex"
"flag" "flag"
@ -10,6 +10,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"math/big" "math/big"
"math/rand"
"os" "os"
"path" "path"
"strconv" "strconv"
@ -20,10 +21,16 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/harmony-one/harmony/api/client"
clientService "github.com/harmony-one/harmony/api/client/service" clientService "github.com/harmony-one/harmony/api/client/service"
proto_node "github.com/harmony-one/harmony/api/proto/node"
"github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/internal/wallet/wallet" nodeconfig "github.com/harmony-one/harmony/internal/configs/node"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/node" "github.com/harmony-one/harmony/node"
"github.com/harmony-one/harmony/p2p"
p2p_host "github.com/harmony-one/harmony/p2p/host"
"github.com/harmony-one/harmony/p2p/p2pimpl"
) )
var ( var (
@ -44,6 +51,10 @@ type AccountState struct {
nonce uint64 nonce uint64
} }
const (
rpcRetry = 3
)
var ( var (
// Account subcommands // Account subcommands
accountImportCommand = flag.NewFlagSet("import", flag.ExitOnError) accountImportCommand = flag.NewFlagSet("import", flag.ExitOnError)
@ -54,7 +65,7 @@ var (
transferSenderPtr = transferCommand.String("from", "0", "Specify the sender account address or index") transferSenderPtr = transferCommand.String("from", "0", "Specify the sender account address or index")
transferReceiverPtr = transferCommand.String("to", "", "Specify the receiver account") transferReceiverPtr = transferCommand.String("to", "", "Specify the receiver account")
transferAmountPtr = transferCommand.Float64("amount", 0, "Specify the amount to transfer") transferAmountPtr = transferCommand.Float64("amount", 0, "Specify the amount to transfer")
transferShardIDPtr = transferCommand.Int("shardID", -1, "Specify the shard ID for the transfer") transferShardIDPtr = transferCommand.Int("shardID", 0, "Specify the shard ID for the transfer")
transferInputDataPtr = transferCommand.String("inputData", "", "Base64-encoded input data to embed in the transaction") transferInputDataPtr = transferCommand.String("inputData", "", "Base64-encoded input data to embed in the transaction")
freeTokenCommand = flag.NewFlagSet("getFreeToken", flag.ExitOnError) freeTokenCommand = flag.NewFlagSet("getFreeToken", flag.ExitOnError)
@ -62,25 +73,38 @@ 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 var (
stopChan = make(chan struct{}) // list of bootnodes
// Print out progress char addrStrings = []string{"/ip4/100.26.90.187/tcp/9876/p2p/QmZJJx6AdaoEkGLrYG4JeLCKeCKDjnFz2wfHNHxAqFSGA9", "/ip4/54.213.43.194/tcp/9876/p2p/QmQayinFSgMMw5cSpDUiD9pQ2WeP6WNmGxpZ6ou3mdVFJX"}
tick = time.NewTicker(time.Second)
// Flag to keep waiting for async call finished // list of rpc servers
async = true // TODO; (leo) take build time parameters or environment parameters to add rpcServers
// Then this can be automated
rpcServers = []struct {
IP string
Port string
}{
{"18.236.96.207", "14555"},
{"18.206.91.170", "14555"},
{"54.213.63.26", "14555"},
{"13.229.96.10", "14555"},
{"34.243.2.56", "14555"},
}
) )
// setupLog setup log for verbose output // setupLog setup log for verbose output
func setupLog() { func setupLog() {
// enable logging for wallet // enable logging for wallet
h := log.StreamHandler(os.Stdout, log.TerminalFormat(false)) h := log.StreamHandler(os.Stdout, log.TerminalFormat(true))
log.Root().SetHandler(h) log.Root().SetHandler(h)
} }
// The main wallet program entrance. Note the this wallet program is for demo-purpose only. It does not implement // The main wallet program entrance. Note the this wallet program is for demo-purpose only. It does not implement
// the secure storage of keys. // the secure storage of keys.
func main() { func main() {
rand.Seed(int64(time.Now().Nanosecond()))
// Verify that a subcommand has been provided // Verify that a subcommand has been provided
// os.Arg[0] is the main command // os.Arg[0] is the main command
@ -118,45 +142,60 @@ func main() {
case "-version": case "-version":
printVersion(os.Args[0]) printVersion(os.Args[0])
case "new": case "new":
go processNewCommnad() processNewCommnad()
case "list": case "list":
go processListCommand() processListCommand()
case "removeAll": case "removeAll":
go clearKeystore() clearKeystore()
fmt.Println("All existing accounts deleted...")
case "import": case "import":
go processImportCommnad() processImportCommnad()
case "balances": case "balances":
go processBalancesCommand() processBalancesCommand()
case "getFreeToken": case "getFreeToken":
go processGetFreeToken() processGetFreeToken()
case "transfer": case "transfer":
go processTransferCommand() 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 // createWalletNode creates wallet server node.
for async { func createWalletNode() *node.Node {
select { bootNodeAddrs, err := utils.StringsToAddrs(addrStrings)
case <-tick.C: if err != nil {
fmt.Printf("=") panic(err)
case <-stopChan:
fmt.Println("Done.")
os.Exit(0)
} }
utils.BootNodes = bootNodeAddrs
shardIDs := []uint32{0}
// dummy host for wallet
// TODO: potentially, too many dummy IP may flush out good IP address from our bootnode DHT
// we need to understand the impact to bootnode DHT with this dummy host ip added
self := p2p.Peer{IP: "127.0.0.1", Port: "6999"}
priKey, _, _ := utils.GenKeyP2P("127.0.0.1", "6999")
host, err := p2pimpl.NewHost(&self, priKey)
if err != nil {
panic(err)
} }
w := node.New(host, nil, nil)
w.Client = client.NewClient(w.GetHost(), shardIDs)
w.NodeConfig.SetRole(nodeconfig.ClientNode)
w.ServiceManagerSetup()
w.RunServices()
return w
} }
func processNewCommnad() { func processNewCommnad() {
randomBytes := [32]byte{} randomBytes := [32]byte{}
_, err := io.ReadFull(rand.Reader, randomBytes[:]) _, err := io.ReadFull(cRand.Reader, randomBytes[:])
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()
@ -166,7 +205,6 @@ 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() {
@ -174,7 +212,6 @@ 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() {
@ -182,7 +219,6 @@ 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() {
@ -194,50 +230,43 @@ func processImportCommnad() {
} }
storePrivateKey(priKeyBytes) storePrivateKey(priKeyBytes)
fmt.Println("Private key imported...") fmt.Println("Private key imported...")
async = false
} }
func processBalancesCommand() { func processBalancesCommand() {
balanceCommand.Parse(os.Args[2:]) balanceCommand.Parse(os.Args[2:])
walletNode := wallet.CreateWalletNode()
if *balanceAddressPtr == "" { if *balanceAddressPtr == "" {
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) {
fmt.Printf(" Balance in Shard %d: %s, nonce: %v \n", shardID, convertBalanceIntoReadableFormat(balanceNonce.balance), balanceNonce.nonce) 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) {
fmt.Printf(" Balance in Shard %d: %s, nonce: %v \n", shardID, convertBalanceIntoReadableFormat(balanceNonce.balance), balanceNonce.nonce) fmt.Printf(" Balance in Shard %d: %s, nonce: %v \n", shardID, convertBalanceIntoReadableFormat(balanceNonce.balance), balanceNonce.nonce)
} }
} }
async = false
} }
func processGetFreeToken() { func processGetFreeToken() {
freeTokenCommand.Parse(os.Args[2:]) freeTokenCommand.Parse(os.Args[2:])
walletNode := wallet.CreateWalletNode()
if *freeTokenAddressPtr == "" { if *freeTokenAddressPtr == "" {
fmt.Println("Error: --address is required") fmt.Println("Error: --address is required")
async = false } else {
return
}
address := common.HexToAddress(*freeTokenAddressPtr) address := common.HexToAddress(*freeTokenAddressPtr)
GetFreeToken(address)
GetFreeToken(address, walletNode) }
async = false
} }
func processTransferCommand() { func processTransferCommand() {
transferCommand.Parse(os.Args[2:]) transferCommand.Parse(os.Args[2:])
if !transferCommand.Parsed() { if !transferCommand.Parsed() {
fmt.Println("Failed to parse flags") fmt.Println("Failed to parse flags")
return
} }
sender := *transferSenderPtr sender := *transferSenderPtr
receiver := *transferReceiverPtr receiver := *transferReceiverPtr
@ -249,24 +278,20 @@ 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)
@ -281,48 +306,45 @@ 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
} }
// Generate transaction // Generate transaction
senderPriKey := priKeys[senderIndex] senderPriKey := priKeys[senderIndex]
senderAddress := addresses[senderIndex] senderAddress := addresses[senderIndex]
walletNode := wallet.CreateWalletNode()
shardIDToAccountState := FetchBalance(senderAddress, walletNode) walletNode := createWalletNode()
shardIDToAccountState := FetchBalance(senderAddress)
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), stopChan) submitTransaction(tx, walletNode, uint32(shardID))
} }
func convertBalanceIntoReadableFormat(balance *big.Int) string { func convertBalanceIntoReadableFormat(balance *big.Int) string {
@ -366,39 +388,54 @@ func convertBalanceIntoReadableFormat(balance *big.Int) string {
} }
// FetchBalance fetches account balance of specified address from the Harmony network // FetchBalance fetches account balance of specified address from the Harmony network
// TODO add support for non beacon chain shards // TODO: (chao) add support for non beacon chain shards
func FetchBalance(address common.Address, walletNode *node.Node) map[uint32]AccountState { func FetchBalance(address common.Address) map[uint32]AccountState {
result := make(map[uint32]AccountState) result := make(map[uint32]AccountState)
peers := wallet.GetPeersFromBeaconChain(walletNode)
if len(peers) == 0 {
fmt.Printf("[FATAL] Can't find peers\n")
return nil
}
peer := peers[0]
port, _ := strconv.Atoi(peer.Port)
client := clientService.NewClient(peer.IP, strconv.Itoa(port+node.ClientServicePortDiff))
response := client.GetBalance(address)
balance := big.NewInt(0) balance := big.NewInt(0)
result[0] = AccountState{balance, 0}
for retry := 0; retry < rpcRetry; retry++ {
server := rpcServers[rand.Intn(len(rpcServers))]
client, err := clientService.NewClient(server.IP, server.Port)
if err != nil {
continue
}
log.Debug("FetchBalance", "server", server)
response, err := client.GetBalance(address)
if err != nil {
log.Info("failed to get balance, retrying ...")
continue
}
log.Debug("FetchBalance", "response", response)
balance.SetBytes(response.Balance) balance.SetBytes(response.Balance)
result[0] = AccountState{balance, response.Nonce} result[0] = AccountState{balance, response.Nonce}
break
}
return result return result
} }
// GetFreeToken requests for token test token on each shard // GetFreeToken requests for token test token on each shard
func GetFreeToken(address common.Address, walletNode *node.Node) { func GetFreeToken(address common.Address) {
peers := wallet.GetPeersFromBeaconChain(walletNode) for retry := 0; retry < rpcRetry; retry++ {
if len(peers) == 0 { server := rpcServers[0]
fmt.Printf("[FATAL] Can't find peers\n") client, err := clientService.NewClient(server.IP, server.Port)
return if err != nil {
continue
} }
peer := peers[0]
port, _ := strconv.Atoi(peer.Port)
client := clientService.NewClient(peer.IP, strconv.Itoa(port+node.ClientServicePortDiff))
response := client.GetFreeToken(address)
log.Debug("GetFreeToken", "server", server)
response, err := client.GetFreeToken(address)
if err != nil {
log.Info("failed to get free token, retrying ...")
continue
}
log.Debug("GetFreeToken", "response", response)
txID := common.Hash{} txID := common.Hash{}
txID.SetBytes(response.TxId) txID.SetBytes(response.TxId)
fmt.Printf("Transaction Id requesting free token in shard %d: %s\n", int(0), txID.Hex()) fmt.Printf("Transaction Id requesting free token in shard %d: %s\n", int(0), txID.Hex())
break
}
} }
// ReadAddresses reads the addresses stored in local keystore // ReadAddresses reads the addresses stored in local keystore
@ -439,6 +476,7 @@ func storePrivateKey(priKey []byte) {
// clearKeystore deletes all data in the local keystore // clearKeystore deletes all data in the local keystore
func clearKeystore() { func clearKeystore() {
ioutil.WriteFile("keystore", []byte{}, 0644) ioutil.WriteFile("keystore", []byte{}, 0644)
fmt.Println("All existing accounts deleted...")
} }
// readPrivateKeys reads all the private key stored in local keystore // readPrivateKeys reads all the private key stored in local keystore
@ -458,3 +496,18 @@ func readPrivateKeys() []*ecdsa.PrivateKey {
} }
return priKeys return priKeys
} }
// submitTransaction submits the transaction to the Harmony network
func submitTransaction(tx *types.Transaction, walletNode *node.Node, shardID uint32) error {
msg := proto_node.ConstructTransactionListMessageAccount(types.Transactions{tx})
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)
return nil
}

@ -1,85 +0,0 @@
package wallet
import (
"fmt"
"time"
"github.com/harmony-one/harmony/api/client"
proto_node "github.com/harmony-one/harmony/api/proto/node"
"github.com/harmony-one/harmony/core/types"
nodeconfig "github.com/harmony-one/harmony/internal/configs/node"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/node"
"github.com/harmony-one/harmony/p2p"
p2p_host "github.com/harmony-one/harmony/p2p/host"
"github.com/harmony-one/harmony/p2p/p2pimpl"
ma "github.com/multiformats/go-multiaddr"
)
// CreateWalletNode creates wallet server node.
func CreateWalletNode() *node.Node {
utils.BootNodes = getBootNodes()
shardIDs := []uint32{0}
// dummy host for wallet
self := p2p.Peer{IP: "127.0.0.1", Port: "6999"}
priKey, _, _ := utils.GenKeyP2P("127.0.0.1", "6999")
host, err := p2pimpl.NewHost(&self, priKey)
if err != nil {
panic(err)
}
walletNode := node.New(host, nil, nil)
walletNode.Client = client.NewClient(walletNode.GetHost(), shardIDs)
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
}
// GetPeersFromBeaconChain get peers from beacon chain
// TODO: add support for normal shards
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))
return true
})
utils.GetLogInstance().Debug("GetPeersFromBeaconChain", "peers:", peers)
return peers
}
// SubmitTransaction submits the transaction to the Harmony network
func SubmitTransaction(tx *types.Transaction, walletNode *node.Node, shardID uint32, stopChan chan struct{}) error {
msg := proto_node.ConstructTransactionListMessageAccount(types.Transactions{tx})
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 {
// 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)
}
return bootNodeAddrs
}

@ -1,47 +0,0 @@
package wallet
import (
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/harmony-one/harmony/api/client"
proto_node "github.com/harmony-one/harmony/api/proto/node"
"github.com/harmony-one/harmony/api/service/networkinfo"
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/node"
"github.com/harmony-one/harmony/p2p"
p2p_host "github.com/harmony-one/harmony/p2p/host"
mock_host "github.com/harmony-one/harmony/p2p/host/mock"
)
func TestCreateWalletNode(test *testing.T) {
// shorten the retry time
networkinfo.ConnectionRetry = 3
walletNode := CreateWalletNode()
if walletNode.Client == nil {
test.Errorf("Wallet node's client is not initialized")
}
}
func TestSubmitTransaction(test *testing.T) {
ctrl := gomock.NewController(test)
defer ctrl.Finish()
m := mock_host.NewMockHost(ctrl)
m.EXPECT().GetSelfPeer().AnyTimes()
walletNode := node.New(m, nil, nil)
walletNode.Client = client.NewClient(walletNode.GetHost(), []uint32{0})
tx := &types.Transaction{}
msg := proto_node.ConstructTransactionListMessageAccount(types.Transactions{tx})
m.EXPECT().SendMessageToGroups([]p2p.GroupID{p2p.GroupIDBeaconClient}, p2p_host.ConstructP2pMessage(byte(0), msg))
SubmitTransaction(tx, walletNode, 0, nil)
time.Sleep(1 * time.Second)
}
Loading…
Cancel
Save