// createSimulationWallet is intended to be called from the rpcclient // and used to create a wallet for actors involved in simulations. func createSimulationWallet(cfg *config) error { // Simulation wallet password is 'password'. privPass := wallet.SimulationPassphrase // Public passphrase is the default. pubPass := []byte(wallet.InsecurePubPassphrase) // Generate a random seed. seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen) if err != nil { return err } netDir := networkDir(cfg.AppDataDir, activeNet.Params) // Write the seed to disk, so that we can restore it later // if need be, for testing purposes. seedStr, err := pgpwordlist.ToStringChecksum(seed) if err != nil { return err } err = ioutil.WriteFile(filepath.Join(netDir, "seed"), []byte(seedStr), 0644) if err != nil { return err } // Create the wallet. dbPath := filepath.Join(netDir, walletDbName) fmt.Println("Creating the wallet...") // Create the wallet database backed by bolt db. db, err := walletdb.Create("bdb", dbPath) if err != nil { return err } defer db.Close() // Create the wallet. err = wallet.Create(db, pubPass, privPass, seed, activeNet.Params, cfg.UnsafeMainNet) if err != nil { return err } fmt.Println("The wallet has been created successfully.") return nil }
// newManager creates a new waddrmgr and imports the given privKey into it. func newManager(t *testing.T, privKeys []string, bs *waddrmgr.BlockStamp) *waddrmgr.Manager { dbPath := filepath.Join(os.TempDir(), "wallet.bin") os.Remove(dbPath) db, err := walletdb.Create("bdb", dbPath) if err != nil { t.Fatal(err) } namespace, err := db.Namespace(waddrmgrNamespaceKey) if err != nil { t.Fatal(err) } seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen) if err != nil { t.Fatal(err) } pubPassphrase := []byte("pub") privPassphrase := []byte("priv") mgr, err := waddrmgr.Create(namespace, seed, pubPassphrase, privPassphrase, &chaincfg.TestNetParams, fastScrypt) if err != nil { t.Fatal(err) } for _, key := range privKeys { wif, err := dcrutil.DecodeWIF(key) if err != nil { t.Fatal(err) } if err = mgr.Unlock(privPassphrase); err != nil { t.Fatal(err) } _, err = mgr.ImportPrivateKey(wif, bs) if err != nil { t.Fatal(err) } } return mgr }
// This example demonstrates how to generate a cryptographically random seed // then use it to create a new master node (extended key). func ExampleNewMaster() { // Generate a random seed at the recommended length. seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen) if err != nil { fmt.Println(err) return } // Generate a new master node using the seed. key, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams) if err != nil { fmt.Println(err) return } // Show that the generated master node extended key is private. fmt.Println("Private Extended Key?:", key.IsPrivate()) // Output: // Private Extended Key?: true }
// TestGenenerateSeed ensures the GenerateSeed function works as intended. func TestGenenerateSeed(t *testing.T) { wantErr := errors.New("seed length must be between 128 and 512 bits") tests := []struct { name string length uint8 err error }{ // Test various valid lengths. {name: "16 bytes", length: 16}, {name: "17 bytes", length: 17}, {name: "20 bytes", length: 20}, {name: "32 bytes", length: 32}, {name: "64 bytes", length: 64}, // Test invalid lengths. {name: "15 bytes", length: 15, err: wantErr}, {name: "65 bytes", length: 65, err: wantErr}, } for i, test := range tests { seed, err := hdkeychain.GenerateSeed(test.length) if !reflect.DeepEqual(err, test.err) { t.Errorf("GenerateSeed #%d (%s): unexpected error -- "+ "want %v, got %v", i, test.name, test.err, err) continue } if test.err == nil && len(seed) != int(test.length) { t.Errorf("GenerateSeed #%d (%s): length mismatch -- "+ "got %d, want %d", i, test.name, len(seed), test.length) continue } } }
// Seed prompts the user whether they want to use an existing wallet generation // seed. When the user answers no, a seed will be generated and displayed to // the user along with prompting them for confirmation. When the user answers // yes, a the user is prompted for it. All prompts are repeated until the user // enters a valid response. The bool returned indicates if the wallet was // restored from a given seed or not. func Seed(reader *bufio.Reader) ([]byte, error) { // Ascertain the wallet generation seed. useUserSeed, err := promptListBool(reader, "Do you have an "+ "existing wallet seed you want to use?", "no") if err != nil { return nil, err } if !useUserSeed { seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen) if err != nil { return nil, err } seedStr, err := pgpwordlist.ToStringChecksum(seed) if err != nil { return nil, err } seedStrSplit := strings.Split(seedStr, " ") fmt.Println("Your wallet generation seed is:") for i := 0; i < hdkeychain.RecommendedSeedLen+1; i++ { fmt.Printf("%v ", seedStrSplit[i]) if (i+1)%6 == 0 { fmt.Printf("\n") } } fmt.Printf("\n\nHex: %x\n", seed) fmt.Println("IMPORTANT: Keep the seed in a safe place as you\n" + "will NOT be able to restore your wallet without it.") fmt.Println("Please keep in mind that anyone who has access\n" + "to the seed can also restore your wallet thereby\n" + "giving them access to all your funds, so it is\n" + "imperative that you keep it in a secure location.") for { fmt.Print(`Once you have stored the seed in a safe ` + `and secure location, enter "OK" to continue: `) confirmSeed, err := reader.ReadString('\n') if err != nil { return nil, err } confirmSeed = strings.TrimSpace(confirmSeed) confirmSeed = strings.Trim(confirmSeed, `"`) if confirmSeed == "OK" { break } } return seed, nil } for { fmt.Print("Enter existing wallet seed " + "(followed by a blank line): ") // Use scanner instead of buffio.Reader so we can choose choose // more complicated ending condition rather than just a single // newline. var seedStr string scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { line := scanner.Text() if line == "" { break } seedStr += " " + line } seedStrTrimmed := strings.TrimSpace(seedStr) seedStrTrimmed = collapseSpace(seedStrTrimmed) wordCount := strings.Count(seedStrTrimmed, " ") + 1 var seed []byte if wordCount == 1 { if len(seedStrTrimmed)%2 != 0 { seedStrTrimmed = "0" + seedStrTrimmed } seed, err = hex.DecodeString(seedStrTrimmed) if err != nil { fmt.Printf("Input error: %v\n", err.Error()) } } else { seed, err = pgpwordlist.ToBytesChecksum(seedStrTrimmed) if err != nil { fmt.Printf("Input error: %v\n", err.Error()) } } if err != nil || len(seed) < hdkeychain.MinSeedBytes || len(seed) > hdkeychain.MaxSeedBytes { fmt.Printf("Invalid seed specified. Must be a "+ "word seed (usually 33 words) using the PGP wordlist or "+ "hexadecimal value that is at least %d bits and "+ "at most %d bits\n", hdkeychain.MinSeedBytes*8, hdkeychain.MaxSeedBytes*8) continue } fmt.Printf("\nSeed input successful. \nHex: %x\n", seed) return seed, nil } }
// createSimulationWallet is intended to be called from the rpcclient // and used to create a wallet for actors involved in simulations. func createSimulationWallet(cfg *config) error { // Simulation wallet password is 'password'. privPass := []byte("password") // Public passphrase is the default. pubPass := []byte(defaultPubPassphrase) // Generate a random seed. seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen) if err != nil { return err } netDir := networkDir(cfg.DataDir, activeNet.Params) // Write the seed to disk, so that we can restore it later // if need be, for testing purposes. seedStr, err := pgpwordlist.ToStringChecksum(seed) if err != nil { return err } err = ioutil.WriteFile(filepath.Join(netDir, "seed"), []byte(seedStr), 0644) if err != nil { return err } // Create the wallet. dbPath := filepath.Join(netDir, walletDbName) fmt.Println("Creating the wallet...") // Create the wallet database backed by bolt db. db, err := walletdb.Create("bdb", dbPath) if err != nil { return err } defer db.Close() // Create the address manager. waddrmgrNamespace, err := db.Namespace(waddrmgrNamespaceKey) if err != nil { return err } manager, err := waddrmgr.Create(waddrmgrNamespace, seed, []byte(pubPass), []byte(privPass), activeNet.Params, nil) if err != nil { return err } defer manager.Close() // Create the stake manager/store. wstakemgrNamespace, err := db.Namespace(wstakemgrNamespaceKey) if err != nil { return err } stakeStore, err := wstakemgr.Create(wstakemgrNamespace, manager, activeNet.Params) if err != nil { return err } defer stakeStore.Close() fmt.Println("The wallet has been created successfully.") return nil }
// TestErrors performs some negative tests for various invalid cases to ensure // the errors are handled properly. func TestErrors(t *testing.T) { // Should get an error when seed has too few bytes. net := &chaincfg.MainNetParams _, err := hdkeychain.NewMaster(bytes.Repeat([]byte{0x00}, 15), net) if err != hdkeychain.ErrInvalidSeedLen { t.Errorf("NewMaster: mismatched error -- got: %v, want: %v", err, hdkeychain.ErrInvalidSeedLen) } // Should get an error when seed has too many bytes. _, err = hdkeychain.NewMaster(bytes.Repeat([]byte{0x00}, 65), net) if err != hdkeychain.ErrInvalidSeedLen { t.Errorf("NewMaster: mismatched error -- got: %v, want: %v", err, hdkeychain.ErrInvalidSeedLen) } // Generate a new key and neuter it to a public extended key. seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen) if err != nil { t.Errorf("GenerateSeed: unexpected error: %v", err) return } extKey, err := hdkeychain.NewMaster(seed, net) if err != nil { t.Errorf("NewMaster: unexpected error: %v", err) return } pubKey, err := extKey.Neuter() if err != nil { t.Errorf("Neuter: unexpected error: %v", err) return } // Deriving a hardened child extended key should fail from a public key. _, err = pubKey.Child(hdkeychain.HardenedKeyStart) if err != hdkeychain.ErrDeriveHardFromPublic { t.Errorf("Child: mismatched error -- got: %v, want: %v", err, hdkeychain.ErrDeriveHardFromPublic) } // NewKeyFromString failure tests. tests := []struct { name string key string err error neuter bool neuterErr error }{ { name: "invalid key length", key: "dpub1234", err: hdkeychain.ErrInvalidKeyLen, }, { name: "bad checksum", key: "dpubZF6AWaFizAuUcbkZSs8cP8Gxzr6Sg5tLYYM7gEjZMC5GDaSHB4rW4F51zkWyo9U19BnXhc99kkEiPg248bYin8m9b8mGss9nxV6N2QpU8vj", err: hdkeychain.ErrBadChecksum, }, { name: "pubkey not on curve", key: "dpubZ9169KDAEUnyoTzA7pDGtXbxpji5LuUk8johUPVGY2CDsz6S7hahGNL6QkeYrUeAPnaJD1MBmrsUnErXScGZdjL6b2gjCRX1Z1GNhLdVCjv", err: errors.New("pubkey [0,50963827496501355358210603252497135226159332537351223778668747140855667399507] isn't on secp256k1 curve"), }, { name: "unsupported version", key: "4s9bfpYH9CkJboPNLFC4BhTENPrjfmKwUxesnqxHBjv585bCLzVdQKuKQ5TouA57FkdDskrR695Z5U2wWwDUUVWXPg7V57sLpc9dMgx74LsVZGEB", err: nil, neuter: true, neuterErr: chaincfg.ErrUnknownHDKeyID, }, } for i, test := range tests { extKey, err := hdkeychain.NewKeyFromString(test.key) if !reflect.DeepEqual(err, test.err) { t.Errorf("NewKeyFromString #%d (%s): mismatched error "+ "-- got: %v, want: %v", i, test.name, err, test.err) continue } if test.neuter { _, err := extKey.Neuter() if !reflect.DeepEqual(err, test.neuterErr) { t.Errorf("Neuter #%d (%s): mismatched error "+ "-- got: %v, want: %v", i, test.name, err, test.neuterErr) continue } } } }