Merge pull request #689 from LeoHChen/wallet_config_file

Wallet config file
pull/692/head
Leo Chen 6 years ago committed by GitHub
commit 886f7ca191
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      .bnkey
  2. 3
      .gitignore
  3. 39
      .hmy/wallet.ini
  4. 113
      cmd/client/wallet/main.go
  5. 1
      go.mod
  6. 66
      internal/utils/configfile.go
  7. 112
      internal/utils/configfile_test.go
  8. 37
      internal/utils/test.ini

@ -0,0 +1,3 @@
{
"key": "CAASpgkwggSiAgEAAoIBAQDhLRM7P8O8U9x+iXoxU+XOv5ihLVe+83he8jVoXp64QHZJQViYj//Wer2qDPeW7hHSC9FU7jsTOBcx3s46Z/4fS+rcXUtGQptscJywjh8wkadSoM8WwnLYIcRIpzI0nbt7wFRUOtwsLhR6/wb2l0qXiygmQn0l+byRQs7Jl1ZwleVetDik/QFGGz8NHFCl2PWNSkkJBOsy2zDGhiInK83qVADlyCP7roLgUe55W625OJ34f9iD7H+/A8UXi7n7BpTmLLDLVS1yeap0Jrftl7fA6s3hDVcNrBBDxsZuntEuKe+5fq6jb4Lj/iHzDoW/mkVWsURR2dIghBCe79cTUjhBAgMBAAECggEAOmOX0wtL1C/iwyUe/F+G/b+M8m5xjGBR3DzuQGwYuCpvAbviH11nt9QIDmpiYU7GbDMISv+jbe5jhVkDHP4OVUs4UYAXntZyuwHQf6+6wrtiKfjs+jfd09GdtkE0sZQdI4/Rzi2dHg+s29/5jEPa4cUB2jnvfcNudDRgrsGBdpvLZCbDm99PAn0z4gV8TZ+JxgMYG3obHjxWbHoyVHXkisM+WDsXaqOsE4xx40P3CevKsbrYYC7la5N/gt4RhkkMF8QGFpiMYHsO8PYxTYpNdZNUgfxLMDqvlUzBhzYE90y+a4bL2rflA+3JI9bfHKFqW0T14QXr8P/s9SqYPd1rgQKBgQDwj+ry71L++Koppct0qnu/IW+sZpw9Oydcaw128fWnEuX/VJR1DR/pBMisCHJwGGBqGQqZntw4Ajg7ZoYx7nlpUv9m5l8GTwS2IrmDkY1gri4qRw2ipzDCah+6mNQeys4B7LbQqbSATdeOQDbbbjJHTreAjts2V+bMx/Tep0G76QKBgQDvoGLmZ39dO5KM7yTG2GM3mmOadOHBk3GY/gjCm43PVtC4RePCIKyRX4UzYZuvmvT++Y5XSH91pVdoNqWY2vOhfBjNufV5tGHkUdTPy5yB6t2B/RvbYOEzZdWU+sQYl+/0EAI8IjoI8j2jnKr4giVVbEhxRU1o2lyj9nDmwpFamQKBgEC+GveC3Y1tky5eRqGBeIh6EToO66G3F+LRlPAcMobimS/crY/LFnl6Jh/WriXpCZnEX4v0q0QPpN6nuxoZGmf0RHSHL6/c+GGketUkCS6p8hbCxLKv2HmaYiuwEfavkj4GXTVPVxro2Eiak6j+wV1bnBtnVywLADzA2/BIh3JxAoGAH95oyNPC8JdXqj6z2W4149M4o/YfgCsn1H0UlS0y8vxMzfUdkVffG4ZkpKy6k+Q76R0vfRQ7P145/bYm1+lmtXdXpSSyLPl8e20WrHwb2Htv8jXDWq3LxZYpjaK8KbkrRH5MjcrPhRkScYwIgPxuEqpQCCB7ZxKDd5ry8P23byECgYBTXNVyeGqn5gBR1zdHzyg8qrUr39oerWqc+ejeDJgXp4HSYmXz7g1KuuE2OgVcOo/gpL13Acjc7neHGHnI20Aa3v5vh3gPohY5lgGy3AIoe0doedhkIUtqWjYkq+bmrrYUf2NG6iNOmjbqjUNBuxuIxlUomvgH+5PqBwGh0yKRMQ=="
}

3
.gitignore vendored

@ -47,9 +47,6 @@ bc_config.json
# leveldb local storage # leveldb local storage
db/ db/
# bootnode keystore
.bnkey
# harmony node keystore # harmony node keystore
.hmykey .hmykey

@ -0,0 +1,39 @@
[default]
bootnode = /ip4/100.26.90.187/tcp/9876/p2p/QmZJJx6AdaoEkGLrYG4JeLCKeCKDjnFz2wfHNHxAqFSGA9
bootnode = /ip4/54.213.43.194/tcp/9876/p2p/QmQayinFSgMMw5cSpDUiD9pQ2WeP6WNmGxpZ6ou3mdVFJX
shards = 1
[default.shard0.rpc]
rpc = 34.217.179.222:14555
rpc = 18.209.247.105:14555
rpc = 100.25.248.42:14555
rpc = 3.80.164.193:14555
rpc = 54.87.237.93:14555
[local]
bootnode = /ip4/127.0.0.1/tcp/19876/p2p/Qmc1V6W7BwX8Ugb42Ti8RnXF1rY5PF7nnZ6bKBryCgi6cv
shards = 1
[local.shard0.rpc]
rpc = 127.0.0.1:14555
rpc = 127.0.0.1:14556
[devnet]
bootnode = /ip4/100.26.90.187/tcp/9871/p2p/Qmdfjtk6hPoyrH1zVD9PEH4zfWLo38dP2mDvvKXfh3tnEv
bootnode = /ip4/54.213.43.194/tcp/9871/p2p/QmRVbTpEYup8dSaURZfF6ByrMTSKa4UyUzJhSjahFzRqNj
shards = 3
[devnet.shard0.rpc]
rpc = 13.57.196.136:14555
rpc = 35.175.103.144:14555
rpc = 54.245.176.36:14555
[devnet.shard1.rpc]
rpc = 35.163.188.234:14555
rpc = 54.215.251.123:14555
rpc = 54.153.11.146:14555
[devnet.shard2.rpc]
rpc = 52.201.246.212:14555
rpc = 3.81.26.139:14555
rpc = 18.237.42.209:14555

@ -53,7 +53,9 @@ type AccountState struct {
} }
const ( const (
rpcRetry = 3 rpcRetry = 3
defaultConfigFile = ".hmy/wallet.ini"
defaultProfile = "default"
) )
var ( var (
@ -77,32 +79,7 @@ var (
) )
var ( var (
// list of bootnodes walletProfile *utils.WalletProfile
addrStrings = []string{"/ip4/100.26.90.187/tcp/9876/p2p/QmZJJx6AdaoEkGLrYG4JeLCKeCKDjnFz2wfHNHxAqFSGA9", "/ip4/54.213.43.194/tcp/9876/p2p/QmQayinFSgMMw5cSpDUiD9pQ2WeP6WNmGxpZ6ou3mdVFJX"}
// list of rpc servers
rpcServers = []p2p.Peer{
p2p.Peer{
IP: "18.236.187.250",
Port: "14555",
},
p2p.Peer{
IP: "54.186.236.223",
Port: "14555",
},
p2p.Peer{
IP: "18.213.246.142",
Port: "14555",
},
p2p.Peer{
IP: "75.101.226.226",
Port: "14555",
},
p2p.Peer{
IP: "34.221.85.140",
Port: "14555",
},
}
) )
// setupLog setup log for verbose output // setupLog setup log for verbose output
@ -122,7 +99,10 @@ func main() {
// os.Arg[1] will be the subcommand // os.Arg[1] will be the subcommand
if len(os.Args) < 2 { if len(os.Args) < 2 {
fmt.Println("Usage:") fmt.Println("Usage:")
fmt.Println(" wallet <action> <params>") fmt.Println(" wallet -p profile <action> <params>")
fmt.Println(" -p profile - Specify the profile of the wallet, either testnet/devnet or others configured. Default is: testnet")
fmt.Println(" The profile is in file:", defaultConfigFile)
fmt.Println()
fmt.Println("Actions:") fmt.Println("Actions:")
fmt.Println(" 1. new - Generates a new account and store the private key locally") fmt.Println(" 1. new - Generates a new account and store the private key locally")
fmt.Println(" 2. list - Lists all accounts in local keystore") fmt.Println(" 2. list - Lists all accounts in local keystore")
@ -133,7 +113,7 @@ func main() {
fmt.Println(" --address - The address to check balance for") fmt.Println(" --address - The address to check balance for")
fmt.Println(" 6. getFreeToken - Gets free token on each shard") fmt.Println(" 6. getFreeToken - Gets free token on each shard")
fmt.Println(" --address - The free token receiver account's address") fmt.Println(" --address - The free token receiver account's address")
fmt.Println(" 7. transfer") fmt.Println(" 7. transfer - Transfer token from one account to another")
fmt.Println(" --from - The sender account's address or index in the local keystore") fmt.Println(" --from - The sender account's address or index in the local keystore")
fmt.Println(" --to - The receiver account's address") fmt.Println(" --to - The receiver account's address")
fmt.Println(" --amount - The amount of token to transfer") fmt.Println(" --amount - The amount of token to transfer")
@ -149,22 +129,22 @@ ARG:
case "--verbose": case "--verbose":
setupLog() setupLog()
os.Args = os.Args[:len(os.Args)-1] os.Args = os.Args[:len(os.Args)-1]
case "--devnet":
// the multiaddress of bootnodes for devnet
addrStrings = []string{"/ip4/100.26.90.187/tcp/9871/p2p/Qmdfjtk6hPoyrH1zVD9PEH4zfWLo38dP2mDvvKXfh3tnEv", "/ip4/54.213.43.194/tcp/9871/p2p/QmRVbTpEYup8dSaURZfF6ByrMTSKa4UyUzJhSjahFzRqNj"}
os.Args = os.Args[:len(os.Args)-1]
default: default:
break ARG break ARG
} }
} }
if len(os.Getenv("RpcNodes")) > 0 { var profile string
rpcServers = utils.StringsToPeers(os.Getenv("RpcNodes")) if os.Args[1] == "-p" {
profile = os.Args[2]
os.Args = os.Args[2:]
} else {
profile = defaultProfile
} }
if len(rpcServers) == 0 { if len(os.Args) == 1 {
fmt.Println("Error: please set environment variable RpcNodes") fmt.Println("Missing action")
fmt.Println("Example: export RpcNodes=127.0.0.1:8000,192.168.0.1:9999") flag.PrintDefaults()
os.Exit(0) os.Exit(1)
} }
// Switch on the subcommand // Switch on the subcommand
@ -180,10 +160,13 @@ ARG:
case "import": case "import":
processImportCommnad() processImportCommnad()
case "balances": case "balances":
readProfile(profile)
processBalancesCommand() processBalancesCommand()
case "getFreeToken": case "getFreeToken":
readProfile(profile)
processGetFreeToken() processGetFreeToken()
case "transfer": case "transfer":
readProfile(profile)
processTransferCommand() processTransferCommand()
default: default:
fmt.Printf("Unknown action: %s\n", os.Args[1]) fmt.Printf("Unknown action: %s\n", os.Args[1])
@ -192,9 +175,19 @@ ARG:
} }
} }
func readProfile(profile string) {
fmt.Printf("Using %s profile for wallet\n", profile)
var err error
walletProfile, err = utils.ReadWalletProfile(defaultConfigFile, profile)
if err != nil {
fmt.Printf("Read wallet profile error: %v\nExiting ...\n", err)
os.Exit(2)
}
}
// createWalletNode creates wallet server node. // createWalletNode creates wallet server node.
func createWalletNode() *node.Node { func createWalletNode() *node.Node {
bootNodeAddrs, err := utils.StringsToAddrs(addrStrings) bootNodeAddrs, err := utils.StringsToAddrs(walletProfile.Bootnodes)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -427,29 +420,30 @@ 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: (chao) add support for non beacon chain shards
func FetchBalance(address common.Address) map[uint32]AccountState { func FetchBalance(address common.Address) map[uint32]AccountState {
result := make(map[uint32]AccountState) result := make(map[uint32]AccountState)
balance := big.NewInt(0) balance := big.NewInt(0)
result[0] = AccountState{balance, 0} for i := 0; i < walletProfile.Shards; i++ {
result[uint32(i)] = AccountState{balance, 0}
for retry := 0; retry < rpcRetry; retry++ {
server := rpcServers[rand.Intn(len(rpcServers))] for retry := 0; retry < rpcRetry; retry++ {
client, err := clientService.NewClient(server.IP, server.Port) server := walletProfile.RPCServer[i][rand.Intn(len(walletProfile.RPCServer[i]))]
if err != nil { client, err := clientService.NewClient(server.IP, server.Port)
continue if err != nil {
} continue
}
log.Debug("FetchBalance", "server", server) log.Debug("FetchBalance", "server", server)
response, err := client.GetBalance(address) response, err := client.GetBalance(address)
if err != nil { if err != nil {
log.Info("failed to get balance, retrying ...") log.Info("failed to get balance, retrying ...")
continue continue
}
log.Debug("FetchBalance", "response", response)
balance.SetBytes(response.Balance)
result[uint32(i)] = AccountState{balance, response.Nonce}
break
} }
log.Debug("FetchBalance", "response", response)
balance.SetBytes(response.Balance)
result[0] = AccountState{balance, response.Nonce}
break
} }
return result return result
} }
@ -457,7 +451,8 @@ func FetchBalance(address common.Address) map[uint32]AccountState {
// GetFreeToken requests for token test token on each shard // GetFreeToken requests for token test token on each shard
func GetFreeToken(address common.Address) { func GetFreeToken(address common.Address) {
for retry := 0; retry < rpcRetry; retry++ { for retry := 0; retry < rpcRetry; retry++ {
server := rpcServers[0] // use the 1st server from shard 0 (beacon chain) to make the getFreeToken call
server := walletProfile.RPCServer[0][0]
client, err := clientService.NewClient(server.IP, server.Port) client, err := clientService.NewClient(server.IP, server.Port)
if err != nil { if err != nil {
continue continue

@ -49,6 +49,7 @@ require (
golang.org/x/tools v0.0.0-20190320160634-b6b7807791df golang.org/x/tools v0.0.0-20190320160634-b6b7807791df
google.golang.org/grpc v1.19.0 google.golang.org/grpc v1.19.0
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127
gopkg.in/ini.v1 v1.42.0
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
gopkg.in/urfave/cli.v1 v1.20.0 // indirect gopkg.in/urfave/cli.v1 v1.20.0 // indirect
) )

@ -0,0 +1,66 @@
package utils
// this module in utils handles the ini file read/write
import (
"fmt"
"strings"
"github.com/harmony-one/harmony/p2p"
ini "gopkg.in/ini.v1"
)
// WalletProfile contains a section and key value pair map
type WalletProfile struct {
Profile string
Bootnodes []string
Shards int
RPCServer [][]p2p.Peer
}
// ReadWalletProfile reads an ini file and return WalletProfile
func ReadWalletProfile(fn string, profile string) (*WalletProfile, error) {
cfg, err := ini.ShadowLoad(fn)
if err != nil {
return nil, err
}
config := new(WalletProfile)
config.Profile = profile
// get the profile section
sec, err := cfg.GetSection(profile)
if err != nil {
return nil, err
}
if sec.HasKey("bootnode") {
config.Bootnodes = sec.Key("bootnode").ValueWithShadows()
} else {
return nil, fmt.Errorf("can't find bootnode key")
}
if sec.HasKey("shards") {
config.Shards = sec.Key("shards").MustInt()
config.RPCServer = make([][]p2p.Peer, config.Shards)
} else {
return nil, fmt.Errorf("can't find shards key")
}
for i := 0; i < config.Shards; i++ {
rpcSec, err := cfg.GetSection(fmt.Sprintf("%s.shard%v.rpc", profile, i))
if err != nil {
return nil, err
}
rpcKey := rpcSec.Key("rpc").ValueWithShadows()
for _, key := range rpcKey {
v := strings.Split(key, ":")
rpc := p2p.Peer{
IP: v[0],
Port: v[1],
}
config.RPCServer[i] = append(config.RPCServer[i], rpc)
}
}
return config, nil
}

@ -0,0 +1,112 @@
package utils
import (
"reflect"
"testing"
"github.com/harmony-one/harmony/p2p"
)
func TestReadWalletProfile(t *testing.T) {
config := []*WalletProfile{
&WalletProfile{
Profile: "default",
Bootnodes: []string{"127.0.0.1:9000/abcd", "127.0.0.1:9999/daeg"},
Shards: 4,
RPCServer: [][]p2p.Peer{
[]p2p.Peer{
p2p.Peer{
IP: "127.0.0.4",
Port: "8888",
},
p2p.Peer{
IP: "192.168.0.4",
Port: "9876",
},
},
[]p2p.Peer{
p2p.Peer{
IP: "127.0.0.1",
Port: "8888",
},
p2p.Peer{
IP: "192.168.0.1",
Port: "9876",
},
},
[]p2p.Peer{
p2p.Peer{
IP: "127.0.0.2",
Port: "8888",
},
p2p.Peer{
IP: "192.168.0.2",
Port: "9876",
},
},
[]p2p.Peer{
p2p.Peer{
IP: "127.0.0.3",
Port: "8888",
},
p2p.Peer{
IP: "192.168.0.3",
Port: "9876",
},
},
},
},
&WalletProfile{
Profile: "testnet",
Bootnodes: []string{"192.168.0.1:9990/abcd", "127.0.0.1:8888/daeg"},
Shards: 3,
RPCServer: [][]p2p.Peer{
[]p2p.Peer{
p2p.Peer{
IP: "192.168.2.3",
Port: "8888",
},
p2p.Peer{
IP: "192.168.192.3",
Port: "9877",
},
},
[]p2p.Peer{
p2p.Peer{
IP: "192.168.2.1",
Port: "8888",
},
p2p.Peer{
IP: "192.168.192.1",
Port: "9877",
},
},
[]p2p.Peer{
p2p.Peer{
IP: "192.168.2.2",
Port: "8888",
},
p2p.Peer{
IP: "192.168.192.2",
Port: "9877",
},
},
},
},
}
config1, err := ReadWalletProfile("test.ini", "default")
if err != nil {
t.Fatalf("ReadWalletProfile Error: %v", err)
}
if !reflect.DeepEqual(config[0], config1) {
t.Errorf("Got: %v\nExpect: %v\n", config1, config[0])
}
config2, err := ReadWalletProfile("test.ini", "testnet")
if err != nil {
t.Fatalf("ReadWalletProfile Error: %v", err)
}
if !reflect.DeepEqual(config[1], config2) {
t.Errorf("Got: %v\nExpect: %v\n", config2, config[1])
}
}

@ -0,0 +1,37 @@
[default]
bootnode = 127.0.0.1:9000/abcd
bootnode = 127.0.0.1:9999/daeg
shards = 4
[default.shard0.rpc]
rpc = 127.0.0.4:8888
rpc = 192.168.0.4:9876
[default.shard1.rpc]
rpc = 127.0.0.1:8888
rpc = 192.168.0.1:9876
[default.shard2.rpc]
rpc = 127.0.0.2:8888
rpc = 192.168.0.2:9876
[default.shard3.rpc]
rpc = 127.0.0.3:8888
rpc = 192.168.0.3:9876
[testnet]
bootnode = 192.168.0.1:9990/abcd
bootnode = 127.0.0.1:8888/daeg
shards = 3
[testnet.shard0.rpc]
rpc = 192.168.2.3:8888
rpc = 192.168.192.3:9877
[testnet.shard1.rpc]
rpc = 192.168.2.1:8888
rpc = 192.168.192.1:9877
[testnet.shard2.rpc]
rpc = 192.168.2.2:8888
rpc = 192.168.192.2:9877
Loading…
Cancel
Save