lbcwallet/walletsetup.go
2016-02-28 22:22:34 -05:00

273 lines
7.8 KiB
Go

// Copyright (c) 2014-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package main
import (
"bufio"
"fmt"
"os"
"path/filepath"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcutil/hdkeychain"
"github.com/btcsuite/btcwallet/internal/legacy/keystore"
"github.com/btcsuite/btcwallet/internal/prompt"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/wallet"
"github.com/btcsuite/btcwallet/walletdb"
_ "github.com/btcsuite/btcwallet/walletdb/bdb"
)
// Namespace keys
var (
waddrmgrNamespaceKey = []byte("waddrmgr")
wtxmgrNamespaceKey = []byte("wtxmgr")
)
// networkDir returns the directory name of a network directory to hold wallet
// files.
func networkDir(dataDir string, chainParams *chaincfg.Params) string {
netname := chainParams.Name
// For now, we must always name the testnet data directory as "testnet"
// and not "testnet3" or any other version, as the chaincfg testnet3
// paramaters will likely be switched to being named "testnet3" in the
// future. This is done to future proof that change, and an upgrade
// plan to move the testnet3 data directory can be worked out later.
if chainParams.Net == wire.TestNet3 {
netname = "testnet"
}
return filepath.Join(dataDir, netname)
}
// convertLegacyKeystore converts all of the addresses in the passed legacy
// key store to the new waddrmgr.Manager format. Both the legacy keystore and
// the new manager must be unlocked.
func convertLegacyKeystore(legacyKeyStore *keystore.Store, manager *waddrmgr.Manager) error {
netParams := legacyKeyStore.Net()
blockStamp := waddrmgr.BlockStamp{
Height: 0,
Hash: *netParams.GenesisHash,
}
for _, walletAddr := range legacyKeyStore.ActiveAddresses() {
switch addr := walletAddr.(type) {
case keystore.PubKeyAddress:
privKey, err := addr.PrivKey()
if err != nil {
fmt.Printf("WARN: Failed to obtain private key "+
"for address %v: %v\n", addr.Address(),
err)
continue
}
wif, err := btcutil.NewWIF((*btcec.PrivateKey)(privKey),
netParams, addr.Compressed())
if err != nil {
fmt.Printf("WARN: Failed to create wallet "+
"import format for address %v: %v\n",
addr.Address(), err)
continue
}
_, err = manager.ImportPrivateKey(wif, &blockStamp)
if err != nil {
fmt.Printf("WARN: Failed to import private "+
"key for address %v: %v\n",
addr.Address(), err)
continue
}
case keystore.ScriptAddress:
_, err := manager.ImportScript(addr.Script(), &blockStamp)
if err != nil {
fmt.Printf("WARN: Failed to import "+
"pay-to-script-hash script for "+
"address %v: %v\n", addr.Address(), err)
continue
}
default:
fmt.Printf("WARN: Skipping unrecognized legacy "+
"keystore type: %T\n", addr)
continue
}
}
return nil
}
// createWallet prompts the user for information needed to generate a new wallet
// and generates the wallet accordingly. The new wallet will reside at the
// provided path.
func createWallet(cfg *config) error {
dbDir := networkDir(cfg.DataDir, activeNet.Params)
loader := wallet.NewLoader(activeNet.Params, dbDir)
// When there is a legacy keystore, open it now to ensure any errors
// don't end up exiting the process after the user has spent time
// entering a bunch of information.
netDir := networkDir(cfg.DataDir, activeNet.Params)
keystorePath := filepath.Join(netDir, keystore.Filename)
var legacyKeyStore *keystore.Store
_, err := os.Stat(keystorePath)
if err != nil && !os.IsNotExist(err) {
// A stat error not due to a non-existant file should be
// returned to the caller.
return err
} else if err == nil {
// Keystore file exists.
legacyKeyStore, err = keystore.OpenDir(netDir)
if err != nil {
return err
}
}
// Start by prompting for the private passphrase. When there is an
// existing keystore, the user will be promped for that passphrase,
// otherwise they will be prompted for a new one.
reader := bufio.NewReader(os.Stdin)
privPass, err := prompt.PrivatePass(reader, legacyKeyStore)
if err != nil {
return err
}
// When there exists a legacy keystore, unlock it now and set up a
// callback to import all keystore keys into the new walletdb
// wallet
if legacyKeyStore != nil {
err = legacyKeyStore.Unlock(privPass)
if err != nil {
return err
}
// Import the addresses in the legacy keystore to the new wallet if
// any exist, locking each wallet again when finished.
loader.RunAfterLoad(func(w *wallet.Wallet, db walletdb.DB) {
defer legacyKeyStore.Lock()
fmt.Println("Importing addresses from existing wallet...")
err := w.Manager.Unlock(privPass)
if err != nil {
fmt.Printf("ERR: Failed to unlock new wallet "+
"during old wallet key import: %v", err)
return
}
defer w.Manager.Lock()
err = convertLegacyKeystore(legacyKeyStore, w.Manager)
if err != nil {
fmt.Printf("ERR: Failed to import keys from old "+
"wallet format: %v", err)
return
}
// Remove the legacy key store.
err = os.Remove(keystorePath)
if err != nil {
fmt.Printf("WARN: Failed to remove legacy wallet "+
"from'%s'\n", keystorePath)
}
})
}
// Ascertain the public passphrase. This will either be a value
// specified by the user or the default hard-coded public passphrase if
// the user does not want the additional public data encryption.
pubPass, err := prompt.PublicPass(reader, privPass,
[]byte(wallet.InsecurePubPassphrase), []byte(cfg.WalletPass))
if err != nil {
return err
}
// Ascertain the wallet generation seed. This will either be an
// automatically generated value the user has already confirmed or a
// value the user has entered which has already been validated.
seed, err := prompt.Seed(reader)
if err != nil {
return err
}
fmt.Println("Creating the wallet...")
w, err := loader.CreateNewWallet(pubPass, privPass, seed)
if err != nil {
return err
}
w.Manager.Close()
fmt.Println("The wallet has been created successfully.")
return 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(wallet.InsecurePubPassphrase)
// Generate a random seed.
seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen)
if err != nil {
return err
}
netDir := networkDir(cfg.DataDir, activeNet.Params)
// 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
}
manager.Close()
fmt.Println("The wallet has been created successfully.")
return nil
}
// checkCreateDir checks that the path exists and is a directory.
// If path does not exist, it is created.
func checkCreateDir(path string) error {
if fi, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
// Attempt data directory creation
if err = os.MkdirAll(path, 0700); err != nil {
return fmt.Errorf("cannot create directory: %s", err)
}
} else {
return fmt.Errorf("error checking directory: %s", err)
}
} else {
if !fi.IsDir() {
return fmt.Errorf("path '%s' is not a directory", path)
}
}
return nil
}