diff --git a/README.md b/README.md
index a6cdac9..f3d5c98 100644
--- a/README.md
+++ b/README.md
@@ -2,16 +2,36 @@
A console bitcoin wallet application written in golang.
+![GoWallet Account View](https://raw.githubusercontent.com/aiportal/gowallet/master/_doc/account.png)
+
**GoWallet uses a secret phrase and a salt phrase to generate your safe wallets.**
Project location: https://github.com/aiportal/gowallet
-Secret at least 16 characters, containing uppercase letters, lowercase letters, numbers, and special characters.
-salt at least 6 characters.
-Secret and salt allow the use of hexadecimal notation similar to '\xff' or '\xFF' to represent a character.
+**GoWallet is a safe brain wallet for bitcoin.**
+ Secret phrase at least 16 characters.
+ Salt phrase at least 6 characters.
+
+ Secret phrases should contain uppercase letters, lowercase letters, numbers, and special characters.
+ Both secret phrases and salt phrases can use hexadecimal notation such as \xff or \xFF to represent a character.
+
+**It is recommended that use a more complex secret and put it on paper.**
+**It's also recommended that keep your salt in mind.**
+
+Donations are welcome at [1BTCzvzTn7QYBwFkRRkXGcVPodwrYoQyAq](https://blockchain.info/address/1BTCzvzTn7QYBwFkRRkXGcVPodwrYoQyAq)
+
+![GoWallet Encryption Process](https://raw.githubusercontent.com/aiportal/gowallet/master/_doc/encryption.png)
+
-**It is advisable to use more complex secret and to write secret on paper.**
-**It is also recommended that salt be memorized in the brain.**
+#### Advanced usage
-Donations are welcome at 1Brn37oiWcDoTVqeP1EzbVtCz3dJ7W1Z57
+You can export bulk wallets using the command line.
-![GoWallet Encryption Process](https://raw.githubusercontent.com/aiportal/gowallet/master/GoWallet.png)
\ No newline at end of file
+ -n or -number uint
+
+ Number of wallets to generate.
+ -v or -vanity string
+
+ Find vanity wallet address matching. (prefix)
+ -e or -export string
+
+ Export wallets(child number, private key and address) in WIF format.
diff --git a/gowallet.go b/gowallet.go
index dac0426..85dbe53 100644
--- a/gowallet.go
+++ b/gowallet.go
@@ -4,124 +4,93 @@ import (
"flag"
"fmt"
"os"
- "gowallet/address"
+ "gowallet/view"
+ "gowallet/wallet"
)
-const goWalletTip = `
-GoWallet uses a secret phrase and a salt phrase to generate your safe wallets.
-Project location: https://github.com/aiportal/gowallet
-
-Secret at least 16 characters, containing uppercase letters, lowercase letters, numbers, and special characters.
-salt at least 6 characters.
-Secret and salt allow the use of hexadecimal notation similar to '\xff' or '\xFF' to represent a character.
-
-It is advisable to use more complex secret and to write secret on paper.
-It is also recommended that salt be memorized in the brain.`
-
-const debug = true
-const trace = false
-
-
func main() {
- vanity, number, export := parseParams()
-
- var passPhrase string
- if _, err := os.Stat("./gowallet.wlt"); os.IsNotExist(err) {
- // New wallets.
- var seed []byte
- if !debug {
- secret, salt, err := address.InputBrainWalletSecret(goWalletTip)
- if err != nil {
- println(err.Error())
- return
- }
- if trace {
- println("your secret is: " + secret)
- println("your salt is: " + salt)
- }
- passPhrase = salt
- seed, err = address.GenerateBrainWalletSeed(secret, salt)
- if err != nil {
- println(err.Error())
- return
- }
- } else {
- seed, err = address.GenerateBrainWalletSeed("https://github.com/aiportal", "gowallet")
- if err != nil {
- println(err.Error())
- return
- }
- passPhrase = "gowallet"
- }
-
- accountKey, accountPub, err := address.GenerateAccount(seed[:], 0)
+ number, vanity, export := parseParams()
+ if number > 0 {
+ err := generateWallets(uint32(number), vanity, export)
if err != nil {
println(err.Error())
return
}
- fmt.Println("")
- fmt.Println("Main account: ")
- // fmt.Printf(" key: %s\n", accountKey)
- fmt.Printf(" pub: %s\n", accountPub)
+ } else {
+ view.ShowSplashView(view.SplashStartView)
- if vanity == "" {
- wallets, err := address.GenerateWallets(accountKey, uint32(number))
+ var ws []*wallet.Wallet
+ if !wallet.IsFileExists() {
+ var err error
+ ws, err = createWallets(1, 10)
if err != nil {
- println(err.Error())
+ fmt.Println(err.Error())
return
}
- for i, w := range wallets {
- encrypt, err := address.EncryptKey(w[0], passPhrase)
- if err != nil {
- println(err.Error())
- encrypt = w[0]
- }
- fmt.Printf("wallet(%d): \n", i)
- fmt.Printf(" private: %s\n", encrypt)
- fmt.Printf(" address: %s\n", w[1])
- }
- if export != "" {
- err := exportWallets(export, wallets)
- if err != nil {
- println(err.Error())
- return
- }
- }
+ // save wallets
+ wf := wallet.NewWalletFile(ws)
+ wf.Save()
} else {
- wallets, err := address.SearchVanities(accountKey, vanity, uint32(number),
- func(i uint32, count uint32, n uint32) {
- fmt.Printf("processed:%d / %d, found: %d \n", i, count, n)
- })
+ wf, err := wallet.LoadWalletFile()
if err != nil {
- println(err.Error())
+ fmt.Println(err.Error())
return
}
- for _, w := range wallets {
- fmt.Printf("wallet(%s): \n", w[2])
- fmt.Printf(" private: %s\n", w[0])
- fmt.Printf(" address: %s\n", w[1])
- }
- if export != "" {
- err := exportWallets(export, wallets)
- if err != nil {
- println(err.Error())
- return
- }
- }
+ ws = wf.Wallets
}
- } else {
- // Open wallets file.
+
+ showUI(ws)
+ }
+}
+
+func showUI(ws []*wallet.Wallet) {
+
+ accountView := view.NewAccountView(ws)
+ accountView.Show()
+
+ for accountView.Data != nil {
+ cmd := accountView.Data.(string)
+ if cmd == "quit" {
+ break
+ }
+ tipView := view.NewTipView(cmd)
+ if tipView != nil {
+ tipView.Show()
+ }
+ accountView.Show()
}
}
+// create wallets by secret and salt
+func createWallets(start, count uint32) (ws []*wallet.Wallet, err error) {
+ view.ShowSplashView(view.SplashCreateView)
+
+ // create wallets
+ wp, err := view.InputNewParameters(3)
+ if err != nil {
+ return
+ }
+ //wp := view.WalletParam{Secret:"https://github.com/aiportal", Salt:"gowallet"}
+
+ wa, err := wallet.NewWalletAccount(wp.SecretBytes(), wp.SaltBytes())
+ if err != nil {
+ return
+ }
+ ws, err = wa.GenerateWallets(start, count)
+ if err != nil {
+ return
+ }
+ return
+}
+
//Parse command line parameters
-func parseParams() (vanity string, number uint, export string) {
+func parseParams() (number uint, vanity, export string) {
- flag.StringVar(&vanity, "vanity", "", "Find vanity wallet address matching. (prefix or regular)")
- flag.StringVar(&vanity, "v", "", "Find vanity wallet address matching. (prefix or regular)")
+ flag.UintVar(&number, "number", 0, "Number of wallets to generate.")
+ flag.UintVar(&number, "n", 0, "Number of wallets to generate.")
- flag.UintVar(&number, "number", 1, "Number of wallets to generate. (default 1)")
- flag.UintVar(&number, "n", 1, "Number of wallets to generate. (default 1)")
+ flag.StringVar(&vanity, "vanity", "", "Find vanity wallet address matching. (prefix)")
+ flag.StringVar(&vanity, "v", "", "Find vanity wallet address matching. (prefix)")
flag.StringVar(&export, "export", "", "Export wallets in WIF format.")
flag.StringVar(&export, "e", "", "Export wallets in WIF format.")
@@ -130,21 +99,53 @@ func parseParams() (vanity string, number uint, export string) {
return
}
-// Export wallets
-func exportWallets(filename string, wallets [][]string) (err error) {
- f, err := os.Create(filename)
+func generateWallets(number uint32, vanity, export string) (err error) {
+
+ view.ShowSplashView(view.SplashStartView)
+ view.ShowSplashView(view.SplashCreateView)
+ wp, err := view.InputNewParameters(3)
if err != nil {
return
}
- defer f.Close()
- for i, w := range wallets {
- if len(w) > 2 {
- f.WriteString(fmt.Sprintf("wallet(%s): \n", w[2]))
- } else {
- f.WriteString(fmt.Sprintf("wallet(%d): \n", i))
+ wa, err := wallet.NewWalletAccount(wp.SecretBytes(), wp.SaltBytes())
+ if err != nil {
+ return
+ }
+ var ws []*wallet.Wallet
+ if vanity == "" {
+ ws, err = wa.GenerateWallets(0, uint32(number))
+ if err != nil {
+ return
+ }
+ } else {
+ var patterns []string
+ patterns, err = wa.NormalizeVanities([]string{vanity})
+ if err != nil {
+ return
+ }
+ ws, err = wa.FindVanities(patterns, func(i, c, n uint32) bool {
+ fmt.Printf("progress: %d, %d, %d\n", i, c, n)
+ return (n >= number)
+ })
+ }
+ if export == "" {
+ for _, w := range ws {
+ fmt.Printf("wallet (%d): \n", w.No)
+ fmt.Println(" " + w.Private)
+ fmt.Println(" " + w.Address)
+ }
+ } else {
+ var f *os.File
+ f, err = os.Create(export)
+ if err != nil {
+ return
+ }
+ defer f.Close()
+ for _, w := range ws {
+ f.WriteString(fmt.Sprintf("wallet(%d): \r\n", w.No))
+ f.WriteString(fmt.Sprintf(" private: %s\r\n", w.Private))
+ f.WriteString(fmt.Sprintf(" address: %s\r\n", w.Address))
}
- f.WriteString(fmt.Sprintf(" private: %s\n", w[0]))
- f.WriteString(fmt.Sprintf(" address: %s\n", w[1]))
}
return
}
diff --git a/wallet/file.go b/wallet/file.go
new file mode 100644
index 0000000..bf20ea2
--- /dev/null
+++ b/wallet/file.go
@@ -0,0 +1,90 @@
+package wallet
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/user"
+)
+
+const WalletFileName = ".gowallet.w02" // version 0.2
+
+// check if user wallet file exists
+func IsFileExists() bool {
+ u, err := user.Current()
+ if err != nil {
+ return false
+ }
+ filename := fmt.Sprintf("%s/%s", u.HomeDir, WalletFileName)
+ f, err := os.Stat(filename)
+ if err != nil {
+ return false
+ }
+ if f.IsDir() {
+ return false
+ }
+ return true
+}
+
+type WalletFile struct {
+ Wallets []*Wallet
+}
+
+func NewWalletFile(ws []*Wallet) *WalletFile {
+ wf := new(WalletFile)
+ wf.Wallets = make([]*Wallet, len(ws))
+ for i, v := range ws {
+ w := NewWallet(v.No, "", v.Address)
+ wf.Wallets[i] = w
+ }
+ return wf
+}
+
+func LoadWalletFile() (wf *WalletFile, err error) {
+ u, err := user.Current()
+ if err != nil {
+ return
+ }
+ path := fmt.Sprintf("%s/%s", u.HomeDir, WalletFileName)
+ _, err = os.Stat(path)
+ if err != nil {
+ return
+ }
+ bs, err := ioutil.ReadFile(path)
+ if err != nil {
+ return
+ }
+ wf = new(WalletFile)
+ err = json.Unmarshal(bs, wf)
+ if err != nil {
+ return
+ }
+ return
+}
+
+func (wf *WalletFile) Save() (err error) {
+ u, err := user.Current()
+ if err != nil {
+ return
+ }
+ path := fmt.Sprintf("%s/%s", u.HomeDir, WalletFileName)
+
+ bs, err := json.Marshal(wf)
+ if err != nil {
+ return
+ }
+ err = ioutil.WriteFile(path, bs, os.ModeExclusive)
+ if err != nil {
+ return
+ }
+ return
+}
+//
+//func (wf *WalletFile) Wallets() (ws []*Wallet) {
+// ws = make([]*Wallet, len(wf.wallets))
+// for i, v := range wf.wallets {
+// ws[i] = v
+// }
+// return
+//}
\ No newline at end of file