-
Notifications
You must be signed in to change notification settings - Fork 499
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #78 from stellar/stellar-hd-wallet
stellar-hd-wallet
- Loading branch information
Showing
9 changed files
with
341 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# Changelog | ||
|
||
All notable changes to this project will be documented in this | ||
file. This project adheres to [Semantic Versioning](http://semver.org/). | ||
|
||
As this project is pre 1.0, breaking changes may happen for minor version | ||
bumps. A breaking change will get clearly notified in this log. | ||
|
||
## [v0.0.1] - 2017-12-28 | ||
|
||
Initial release. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# stellar-hd-wallet | ||
|
||
Console tool to generate Stellar HD wallet for a given seed. Implements [SEP-0005](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0005.md). | ||
|
||
This is experimental software. Use at your own risk. | ||
|
||
## Usage | ||
|
||
``` | ||
Simple HD wallet for Stellar Lumens. THIS PROGRAM IS STILL EXPERIMENTAL. USE AT YOUR OWN RISK. | ||
Usage: | ||
stellar-hd-wallet [command] | ||
Available Commands: | ||
accounts Display accounts for a given mnemonic code | ||
new Generates a new mnemonic code | ||
Flags: | ||
-h, --help help for stellar-hd-wallet | ||
Use "stellar-hd-wallet [command] --help" for more information about a command. | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package commands | ||
|
||
import ( | ||
"encoding/hex" | ||
"fmt" | ||
"regexp" | ||
"strings" | ||
|
||
"github.com/bartekn/go-bip39" | ||
"github.com/spf13/cobra" | ||
"github.com/stellar/go/exp/crypto/derivation" | ||
"github.com/stellar/go/keypair" | ||
"github.com/stellar/go/support/errors" | ||
) | ||
|
||
var wordsRegexp = regexp.MustCompile(`^[a-z]+$`) | ||
var count, startID uint32 | ||
|
||
var allowedNumbers = map[uint32]bool{12: true, 15: true, 18: true, 21: true, 24: true} | ||
|
||
var AccountsCmd = &cobra.Command{ | ||
Use: "accounts", | ||
Short: "Display accounts for a given mnemonic code", | ||
Long: "", | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
printf("How many words? ") | ||
wordsCount := readUint() | ||
if _, exist := allowedNumbers[wordsCount]; !exist { | ||
return errors.New("Invalid value, allowed values: 12, 15, 18, 21, 24") | ||
} | ||
|
||
words := make([]string, wordsCount) | ||
for i := uint32(0); i < wordsCount; i++ { | ||
printf("Enter word #%-4d", i+1) | ||
words[i] = readString() | ||
if !wordsRegexp.MatchString(words[i]) { | ||
println("Invalid word, try again.") | ||
i-- | ||
} | ||
} | ||
|
||
printf("Enter password (leave empty if none): ") | ||
password := readString() | ||
|
||
mnemonic := strings.Join(words, " ") | ||
println("Mnemonic:", mnemonic) | ||
|
||
seed, err := bip39.NewSeedWithErrorChecking(mnemonic, password) | ||
if err != nil { | ||
return errors.New("Invalid words or checksum") | ||
} | ||
|
||
println("BIP39 Seed:", hex.EncodeToString(seed)) | ||
|
||
masterKey, err := derivation.DeriveForPath(derivation.StellarAccountPrefix, seed) | ||
if err != nil { | ||
return errors.Wrap(err, "Error deriving master key") | ||
} | ||
|
||
println("m/44'/148' key:", hex.EncodeToString(masterKey.Key)) | ||
|
||
println("") | ||
|
||
for i := uint32(startID); i < startID+count; i++ { | ||
key, err := masterKey.Derive(derivation.FirstHardenedIndex + i) | ||
if err != nil { | ||
return errors.Wrap(err, "Error deriving child key") | ||
} | ||
|
||
kp, err := keypair.FromRawSeed(key.RawSeed()) | ||
if err != nil { | ||
return errors.Wrap(err, "Error creating key pair") | ||
} | ||
|
||
println(fmt.Sprintf(derivation.StellarAccountPathFormat, i), kp.Address(), kp.Seed()) | ||
} | ||
|
||
return nil | ||
}, | ||
} | ||
|
||
func init() { | ||
AccountsCmd.Flags().Uint32VarP(&count, "count", "c", 10, "number of accounts to display") | ||
AccountsCmd.Flags().Uint32VarP(&startID, "start", "s", 0, "ID of the first wallet to display") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package commands | ||
|
||
import ( | ||
"bufio" | ||
"bytes" | ||
"fmt" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestAccounts(t *testing.T) { | ||
tests := []struct { | ||
Words string | ||
Passphrase string | ||
Error string | ||
Want string | ||
}{ | ||
{ | ||
Words: "illness spike retreat truth genius clock brain pass fit cave bargain toe", | ||
Want: `m/44'/148'/0' GDRXE2BQUC3AZNPVFSCEZ76NJ3WWL25FYFK6RGZGIEKWE4SOOHSUJUJ6 SBGWSG6BTNCKCOB3DIFBGCVMUPQFYPA2G4O34RMTB343OYPXU5DJDVMN | ||
m/44'/148'/1' GBAW5XGWORWVFE2XTJYDTLDHXTY2Q2MO73HYCGB3XMFMQ562Q2W2GJQX SCEPFFWGAG5P2VX5DHIYK3XEMZYLTYWIPWYEKXFHSK25RVMIUNJ7CTIS | ||
m/44'/148'/2' GAY5PRAHJ2HIYBYCLZXTHID6SPVELOOYH2LBPH3LD4RUMXUW3DOYTLXW SDAILLEZCSA67DUEP3XUPZJ7NYG7KGVRM46XA7K5QWWUIGADUZCZWTJP | ||
m/44'/148'/3' GAOD5NRAEORFE34G5D4EOSKIJB6V4Z2FGPBCJNQI6MNICVITE6CSYIAE SBMWLNV75BPI2VB4G27RWOMABVRTSSF7352CCYGVELZDSHCXWCYFKXIX | ||
m/44'/148'/4' GBCUXLFLSL2JE3NWLHAWXQZN6SQC6577YMAU3M3BEMWKYPFWXBSRCWV4 SCPCY3CEHMOP2TADSV2ERNNZBNHBGP4V32VGOORIEV6QJLXD5NMCJUXI | ||
m/44'/148'/5' GBRQY5JFN5UBG5PGOSUOL4M6D7VRMAYU6WW2ZWXBMCKB7GPT3YCBU2XZ SCK27SFHI3WUDOEMJREV7ZJQG34SCBR6YWCE6OLEXUS2VVYTSNGCRS6X | ||
m/44'/148'/6' GBY27SJVFEWR3DUACNBSMJB6T4ZPR4C7ZXSTHT6GMZUDL23LAM5S2PQX SDJ4WDPOQAJYR3YIAJOJP3E6E4BMRB7VZ4QAEGCP7EYVDW6NQD3LRJMZ | ||
m/44'/148'/7' GAY7T23Z34DWLSTEAUKVBPHHBUE4E3EMZBAQSLV6ZHS764U3TKUSNJOF SA3HXJUCE2N27TBIZ5JRBLEBF3TLPQEBINP47E6BTMIWW2RJ5UKR2B3L | ||
m/44'/148'/8' GDJTCF62UUYSAFAVIXHPRBR4AUZV6NYJR75INVDXLLRZLZQ62S44443R SCD5OSHUUC75MSJG44BAT3HFZL2HZMMQ5M4GPDL7KA6HJHV3FLMUJAME | ||
m/44'/148'/9' GBTVYYDIYWGUQUTKX6ZMLGSZGMTESJYJKJWAATGZGITA25ZB6T5REF44 SCJGVMJ66WAUHQHNLMWDFGY2E72QKSI3XGSBYV6BANDFUFE7VY4XNXXR`, | ||
}, | ||
{ | ||
Words: "resource asthma orphan phone ice canvas fire useful arch jewel impose vague theory cushion top", | ||
Want: `m/44'/148'/0' GAVXVW5MCK7Q66RIBWZZKZEDQTRXWCZUP4DIIFXCCENGW2P6W4OA34RH SAKS7I2PNDBE5SJSUSU2XLJ7K5XJ3V3K4UDFAHMSBQYPOKE247VHAGDB | ||
m/44'/148'/1' GDFCYVCICATX5YPJUDS22KM2GW5QU2KKSPPPT2IC5AQIU6TP3BZSLR5K SAZ2H5GLAVWCUWNPQMB6I3OHRI63T2ACUUAWSH7NAGYYPXGIOPLPW3Q4 | ||
m/44'/148'/2' GAUA3XK3SGEQFNCBM423WIM5WCZ4CR4ZDPDFCYSFLCTODGGGJMPOHAAE SDVSSLPL76I33DKAI4LFTOAKCHJNCXUERGPCMVFT655Z4GRLWM6ZZTSC | ||
m/44'/148'/3' GAH3S77QXTAPZ77REY6LGFIJ2XWVXFOKXHCFLA6HQTL3POLVZJDHHUDM SCH56YSGOBYVBC6DO3ZI2PY62GBVXT4SEJSXJOBQYGC2GCEZSB5PEVBZ | ||
m/44'/148'/4' GCSCZVGV2Y3EQ2RATJ7TE6PVWTW5OH5SMG754AF6W6YM3KJF7RMNPB4Y SBWBM73VUNBGBMFD4E2BA7Q756AKVEAAVTQH34RYEUFD6X64VYL5KXQ2 | ||
m/44'/148'/5' GDKWYAJE3W6PWCXDZNMFNFQSPTF6BUDANE6OVRYMJKBYNGL62VKKCNCC SAVS4CDQZI6PSA5DPCC42S5WLKYIPKXPCJSFYY4N3VDK25T2XX2BTGVX | ||
m/44'/148'/6' GCDTVB4XDLNX22HI5GUWHBXJFBCPB6JNU6ZON7E57FA3LFURS74CWDJH SDFC7WZT3GDQVQUQMXN7TC7UWDW5E3GSMFPHUT2TSTQ7RKWTRA4PLBAL | ||
m/44'/148'/7' GBTDPL5S4IOUQHDLCZ7I2UXJ2TEHO6DYIQ3F2P5OOP3IS7JSJI4UMHQJ SA6UO2FIYC6AS2MSDECLR6F7NKCJTG67F7R4LV2GYB4HCZYXJZRLPOBB | ||
m/44'/148'/8' GD3KWA24OIM7V3MZKDAVSLN3NBHGKVURNJ72ZCTAJSDTF7RIGFXPW5FQ SBDNHDDICLLMBIDZ2IF2D3LH44OVUGGAVHQVQ6BZQI5IQO6AB6KNJCOV | ||
m/44'/148'/9' GB3C6RRQB3V7EPDXEDJCMTS45LVDLSZQ46PTIGKZUY37DXXEOAKJIWSV SDHRG2J34MGDAYHMOVKVJC6LX2QZMCTIKRO5I4JQ6BJQ36KVL6QUTT72`, | ||
}, | ||
{ | ||
Words: "bench hurt jump file august wise shallow faculty impulse spring exact slush thunder author capable act festival slice deposit sauce coconut afford frown better", | ||
Want: `m/44'/148'/0' GC3MMSXBWHL6CPOAVERSJITX7BH76YU252WGLUOM5CJX3E7UCYZBTPJQ SAEWIVK3VLNEJ3WEJRZXQGDAS5NVG2BYSYDFRSH4GKVTS5RXNVED5AX7 | ||
m/44'/148'/1' GB3MTYFXPBZBUINVG72XR7AQ6P2I32CYSXWNRKJ2PV5H5C7EAM5YYISO SBKSABCPDWXDFSZISAVJ5XKVIEWV4M5O3KBRRLSPY3COQI7ZP423FYB4 | ||
m/44'/148'/2' GDYF7GIHS2TRGJ5WW4MZ4ELIUIBINRNYPPAWVQBPLAZXC2JRDI4DGAKU SD5CCQAFRIPB3BWBHQYQ5SC66IB2AVMFNWWPBYGSUXVRZNCIRJ7IHESQ | ||
m/44'/148'/3' GAFLH7DGM3VXFVUID7JUKSGOYG52ZRAQPZHQASVCEQERYC5I4PPJUWBD SBSGSAIKEF7JYQWQSGXKB4SRHNSKDXTEI33WZDRR6UHYQCQ5I6ZGZQPK | ||
m/44'/148'/4' GAXG3LWEXWCAWUABRO6SMAEUKJXLB5BBX6J2KMHFRIWKAMDJKCFGS3NN SBIZH53PIRFTPI73JG7QYA3YAINOAT2XMNAUARB3QOWWVZVBAROHGXWM | ||
m/44'/148'/5' GA6RUD4DZ2NEMAQY4VZJ4C6K6VSEYEJITNSLUQKLCFHJ2JOGC5UCGCFQ SCVM6ZNVRUOP4NMCMMKLTVBEMAF2THIOMHPYSSMPCD2ZU7VDPARQQ6OY | ||
m/44'/148'/6' GCUDW6ZF5SCGCMS3QUTELZ6LSAH6IVVXNRPRLAUNJ2XYLCA7KH7ZCVQS SBSHUZQNC45IAIRSAHMWJEJ35RY7YNW6SMOEBZHTMMG64NKV7Y52ZEO2 | ||
m/44'/148'/7' GBJ646Q524WGBN5X5NOAPIF5VQCR2WZCN6QZIDOSY6VA2PMHJ2X636G4 SC2QO2K2B4EBNBJMBZIKOYSHEX4EZAZNIF4UNLH63AQYV6BE7SMYWC6E | ||
m/44'/148'/8' GDHX4LU6YBSXGYTR7SX2P4ZYZSN24VXNJBVAFOB2GEBKNN3I54IYSRM4 SCGMC5AHAAVB3D4JXQPCORWW37T44XJZUNPEMLRW6DCOEARY3H5MAQST | ||
m/44'/148'/9' GDXOY6HXPIDT2QD352CH7VWX257PHVFR72COWQ74QE3TEV4PK2KCKZX7 SCPA5OX4EYINOPAUEQCPY6TJMYICUS5M7TVXYKWXR3G5ZRAJXY3C37GF`, | ||
}, | ||
{ | ||
Words: "cable spray genius state float twenty onion head street palace net private method loan turn phrase state blanket interest dry amazing dress blast tube", | ||
Passphrase: "p4ssphr4se", | ||
Want: `m/44'/148'/0' GDAHPZ2NSYIIHZXM56Y36SBVTV5QKFIZGYMMBHOU53ETUSWTP62B63EQ SAFWTGXVS7ELMNCXELFWCFZOPMHUZ5LXNBGUVRCY3FHLFPXK4QPXYP2X | ||
m/44'/148'/1' GDY47CJARRHHL66JH3RJURDYXAMIQ5DMXZLP3TDAUJ6IN2GUOFX4OJOC SBQPDFUGLMWJYEYXFRM5TQX3AX2BR47WKI4FDS7EJQUSEUUVY72MZPJF | ||
m/44'/148'/2' GCLAQF5H5LGJ2A6ACOMNEHSWYDJ3VKVBUBHDWFGRBEPAVZ56L4D7JJID SAF2LXRW6FOSVQNC4HHIIDURZL4SCGCG7UEGG23ZQG6Q2DKIGMPZV6BZ | ||
m/44'/148'/3' GBC36J4KG7ZSIQ5UOSJFQNUP4IBRN6LVUFAHQWT2ODEQ7Y3ASWC5ZN3B SDCCVBIYZDMXOR4VPC3IYMIPODNEDZCS44LDN7B5ZWECIE57N3BTV4GQ | ||
m/44'/148'/4' GA6NHA4KPH5LFYD6LZH35SIX3DU5CWU3GX6GCKPJPPTQCCQPP627E3CB SA5TRXTO7BG2Z6QTQT3O2LC7A7DLZZ2RBTGUNCTG346PLVSSHXPNDVNT | ||
m/44'/148'/5' GBOWMXTLABFNEWO34UJNSJJNVEF6ESLCNNS36S5SX46UZT2MNYJOLA5L SDEOED2KPHV355YNOLLDLVQB7HDPQVIGKXCAJMA3HTM4325ZHFZSKKUC | ||
m/44'/148'/6' GBL3F5JUZN3SQKZ7SL4XSXEJI2SNSVGO6WZWNJLG666WOJHNDDLEXTSZ SDYNO6TLFNV3IM6THLNGUG5FII4ET2H7NH3KCT6OAHIUSHKR4XBEEI6A | ||
m/44'/148'/7' GA5XPPWXL22HFFL5K5CE37CEPUHXYGSP3NNWGM6IK6K4C3EFHZFKSAND SDXMJXAY45W3WEFWMYEPLPIF4CXAD5ECQ37XKMGY5EKLM472SSRJXCYD | ||
m/44'/148'/8' GDS5I7L7LWFUVSYVAOHXJET2565MGGHJ4VHGVJXIKVKNO5D4JWXIZ3XU SAIZA26BUP55TDCJ4U7I2MSQEAJDPDSZSBKBPWQTD5OQZQSJAGNN2IQB | ||
m/44'/148'/9' GBOSMFQYKWFDHJWCMCZSMGUMWCZOM4KFMXXS64INDHVCJ2A2JAABCYRR SDXDYPDNRMGOF25AWYYKPHFAD3M54IT7LCLG7RWTGR3TS32A4HTUXNOS`, | ||
}, | ||
{ | ||
Words: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", | ||
Want: `m/44'/148'/0' GB3JDWCQJCWMJ3IILWIGDTQJJC5567PGVEVXSCVPEQOTDN64VJBDQBYX SBUV3MRWKNS6AYKZ6E6MOUVF2OYMON3MIUASWL3JLY5E3ISDJFELYBRZ | ||
m/44'/148'/1' GDVSYYTUAJ3ACHTPQNSTQBDQ4LDHQCMNY4FCEQH5TJUMSSLWQSTG42MV SCHDCVCWGAKGIMTORV6K5DYYV3BY4WG3RA4M6MCBGJLHUCWU2MC6DL66 | ||
m/44'/148'/2' GBFPWBTN4AXHPWPTQVQBP4KRZ2YVYYOGRMV2PEYL2OBPPJDP7LECEVHR SAPLVTLUXSDLFRDGCCFLPDZMTCEVMP3ZXTM74EBJCVKZKM34LGQPF7K3 | ||
m/44'/148'/3' GCCCOWAKYVFY5M6SYHOW33TSNC7Z5IBRUEU2XQVVT34CIZU7CXZ4OQ4O SDQYXOP2EAUZP4YOEQ5BUJIQ3RDSP5XV4ZFI6C5Y3QCD5Y63LWPXT7PW | ||
m/44'/148'/4' GCQ3J35MKPKJX7JDXRHC5YTXTULFMCBMZ5IC63EDR66QA3LO7264ZL7Q SCT7DUHYZD6DRCETT6M73GWKFJI4D56P3SNWNWNJ7ANLJZS6XIFYYXSB | ||
m/44'/148'/5' GDTA7622ZA5PW7F7JL7NOEFGW62M7GW2GY764EQC2TUJ42YJQE2A3QUL SDTWG5AFDI6GRQNLPWOC7IYS7AKOGMI2GX4OXTBTZHHYPMNZ2PX4ONWU | ||
m/44'/148'/6' GD7A7EACTPTBCYCURD43IEZXGIBCEXNBHN3OFWV2FOX67XKUIGRCTBNU SDJMWY4KFRS4PTA5WBFVCPS2GKYLXOMCLQSBNEIBG7KRGHNQOM25KMCP | ||
m/44'/148'/7' GAF4AGPVLQXFKEWQV3DZU5YEFU6YP7XJHAEEQH4G3R664MSF77FLLRK3 SDOJH5JRCNGT57QTPTJEQGBEBZJPXE7XUDYDB24VTOPP7PH3ALKHAHFG | ||
m/44'/148'/8' GABTYCZJMCP55SS6I46SR76IHETZDLG4L37MLZRZKQDGBLS5RMP65TSX SC6N6GYQ2VA4T7CUP2BWGBRT2P6L2HQSZIUNQRHNDLISF6ND7TW4P4ER | ||
m/44'/148'/9' GAKFARYSPI33KUJE7HYLT47DCX2PFWJ77W3LZMRBPSGPGYPMSDBE7W7X SALJ5LPBTXCFML2CQ7ORP7WJNJOZSVBVRQAAODMVHMUF4P4XXFZB7MKY`, | ||
}, | ||
// Invalid: | ||
{ | ||
Words: "illness spike retreat truth genius clock brain pass fit cave bargain illness", | ||
Error: "Invalid words or checksum", | ||
}, | ||
} | ||
|
||
for _, test := range tests { | ||
|
||
t.Run(fmt.Sprintf("words %s passphrase %s", test.Words, test.Passphrase), func(t *testing.T) { | ||
words := strings.Split(test.Words, " ") | ||
input := fmt.Sprintf("%d\n%s\n%s\n", len(words), strings.Join(words, "\n"), test.Passphrase) | ||
|
||
// Global variables, AFAIK there is no elegant way to pass it to cobra.Command | ||
reader = bufio.NewReader(bytes.NewBufferString(input)) | ||
out = &bytes.Buffer{} | ||
|
||
err := AccountsCmd.RunE(nil, []string{}) | ||
if test.Error != "" { | ||
assert.Error(t, err) | ||
assert.Contains(t, err.Error(), test.Error) | ||
} else { | ||
assert.NoError(t, err) | ||
output := out.(*bytes.Buffer).String() | ||
assert.Contains(t, output, test.Want) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package commands | ||
|
||
import ( | ||
"bufio" | ||
"fmt" | ||
"io" | ||
"log" | ||
"os" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
var reader = bufio.NewReader(os.Stdin) | ||
var out io.Writer = os.Stdout | ||
|
||
func readString() string { | ||
line, _ := reader.ReadString('\n') | ||
return strings.TrimRight(line, "\n") | ||
} | ||
|
||
func readUint() uint32 { | ||
line := readString() | ||
number, err := strconv.Atoi(line) | ||
if err != nil { | ||
log.Fatal("Invalid value") | ||
} | ||
|
||
return uint32(number) | ||
} | ||
|
||
func printf(format string, a ...interface{}) { | ||
fmt.Fprintf(out, format, a...) | ||
} | ||
|
||
func println(a ...interface{}) { | ||
fmt.Fprintln(out, a...) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package commands | ||
|
||
import ( | ||
"strings" | ||
|
||
"github.com/bartekn/go-bip39" | ||
"github.com/spf13/cobra" | ||
"github.com/stellar/go/support/errors" | ||
) | ||
|
||
const DefaultEntropySize = 256 | ||
|
||
var NewCmd = &cobra.Command{ | ||
Use: "new", | ||
Short: "Generates a new mnemonic code", | ||
Long: "", | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
entropy, err := bip39.NewEntropy(DefaultEntropySize) | ||
if err != nil { | ||
return errors.Wrap(err, "Error generating entropy") | ||
} | ||
|
||
mnemonic, err := bip39.NewMnemonic(entropy) | ||
if err != nil { | ||
return errors.Wrap(err, "Error generating mnemonic code") | ||
} | ||
|
||
words := strings.Split(mnemonic, " ") | ||
for i := 0; i < len(words); i++ { | ||
printf("word %02d/24: %10s", i+1, words[i]) | ||
readString() | ||
} | ||
|
||
println("WARNING! Store the words above in a safe place!") | ||
println("WARNING! If you lose your words, you will lose access to funds in all derived accounts!") | ||
println("WARNING! Anyone who has access to these words can spend your funds!") | ||
println("") | ||
println("Use: `stellar-hd-wallet accounts` command to see generated accounts.") | ||
|
||
return nil | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package main | ||
|
||
import ( | ||
"log" | ||
|
||
"github.com/spf13/cobra" | ||
"github.com/stellar/go/tools/stellar-hd-wallet/commands" | ||
) | ||
|
||
var mainCmd = &cobra.Command{ | ||
Use: "stellar-hd-wallet", | ||
Short: "Simple HD wallet for Stellar Lumens. THIS PROGRAM IS STILL EXPERIMENTAL. USE AT YOUR OWN RISK.", | ||
} | ||
|
||
func init() { | ||
mainCmd.AddCommand(commands.NewCmd) | ||
mainCmd.AddCommand(commands.AccountsCmd) | ||
} | ||
|
||
func main() { | ||
if err := mainCmd.Execute(); err != nil { | ||
log.Fatal(err) | ||
} | ||
} |