Add conversion from legacy keystore if it exists.

This commit is contained in:
Dave Collins 2014-10-01 12:15:32 -05:00
parent 8f9f53a618
commit b44f48882f

View file

@ -24,7 +24,10 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcutil/hdkeychain" "github.com/btcsuite/btcutil/hdkeychain"
"github.com/btcsuite/btcwallet/legacy/keystore"
"github.com/btcsuite/btcwallet/waddrmgr" "github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/walletdb" "github.com/btcsuite/btcwallet/walletdb"
_ "github.com/btcsuite/btcwallet/walletdb/bdb" _ "github.com/btcsuite/btcwallet/walletdb/bdb"
@ -80,7 +83,7 @@ func promptConsoleListBool(reader *bufio.Reader, prefix string, defaultEntry str
// promptConsolePass prompts the user for a passphrase with the given prefix. // promptConsolePass prompts the user for a passphrase with the given prefix.
// The function will ask the user to confirm the passphrase and will repeat // The function will ask the user to confirm the passphrase and will repeat
// the prompts until they enter a matching response. // the prompts until they enter a matching response.
func promptConsolePass(reader *bufio.Reader, prefix string) (string, error) { func promptConsolePass(reader *bufio.Reader, prefix string, confirm bool) (string, error) {
// Prompt the user until they enter a passphrase. // Prompt the user until they enter a passphrase.
prompt := fmt.Sprintf("%s: ", prefix) prompt := fmt.Sprintf("%s: ", prefix)
for { for {
@ -94,6 +97,10 @@ func promptConsolePass(reader *bufio.Reader, prefix string) (string, error) {
continue continue
} }
if !confirm {
return pass, nil
}
fmt.Print("Confirm passphrase: ") fmt.Print("Confirm passphrase: ")
confirm, err := reader.ReadString('\n') confirm, err := reader.ReadString('\n')
if err != nil { if err != nil {
@ -109,6 +116,47 @@ func promptConsolePass(reader *bufio.Reader, prefix string) (string, error) {
} }
} }
// promptConsolePrivatePass prompts the user for a private passphrase with
// varying behavior depending on whether the passed legacy keystore exists.
// When it does, the user is prompted for the existing passphrase which is then
// used to unlock it. On the other hand, when the legacy keystore is nil, the
// user is prompted for a new private passphrase. All prompts are repeated
// until the user enters a valid response.
func promptConsolePrivatePass(reader *bufio.Reader, legacyKeyStore *keystore.Store) (string, error) {
// When there is not an existing legacy wallet, simply prompt the user
// for a new private passphase and return it.
if legacyKeyStore == nil {
return promptConsolePass(reader, "Enter the private "+
"passphrase for your new wallet", true)
}
// At this point, there is an existing legacy wallet, so prompt the user
// for the existing private passphrase and ensure it properly unlocks
// the legacy wallet so all of the addresses can later be imported.
fmt.Println("You have an existing legacy wallet. All addresses from " +
"your existing legacy wallet will be imported into the new " +
"wallet format.")
for {
privPass, err := promptConsolePass(reader, "Enter the private "+
"passphrase for your existing wallet", false)
if err != nil {
return "", err
}
// Keep prompting the user until the passphrase is correct.
if err := legacyKeyStore.Unlock([]byte(privPass)); err != nil {
if err == keystore.ErrWrongPassphrase {
fmt.Println(err)
continue
}
return "", err
}
return privPass, nil
}
}
// promptConsolePublicPass prompts the user whether they want to add an // promptConsolePublicPass prompts the user whether they want to add an
// additional layer of encryption to the wallet. When the user answers yes and // additional layer of encryption to the wallet. When the user answers yes and
// there is already a public passphrase provided via the passed config, it // there is already a public passphrase provided via the passed config, it
@ -145,7 +193,7 @@ func promptConsolePublicPass(reader *bufio.Reader, privPass string, cfg *config)
for { for {
pubPass, err = promptConsolePass(reader, "Enter the public "+ pubPass, err = promptConsolePass(reader, "Enter the public "+
"passphrase for your new wallet") "passphrase for your new wallet", true)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -240,14 +288,85 @@ func promptConsoleSeed(reader *bufio.Reader) ([]byte, error) {
} }
} }
// 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 // createWallet prompts the user for information needed to generate a new wallet
// and generates the wallet accordingly. The new wallet will reside at the // and generates the wallet accordingly. The new wallet will reside at the
// provided path. // provided path.
func createWallet(cfg *config) error { func createWallet(cfg *config) error {
// Start by prompting for the private passphrase. // 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
if fileExists(keystorePath) {
var err error
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) reader := bufio.NewReader(os.Stdin)
privPass, err := promptConsolePass(reader, "Enter the private passphrase "+ privPass, err := promptConsolePrivatePass(reader, legacyKeyStore)
"for your new wallet")
if err != nil { if err != nil {
return err return err
} }
@ -269,7 +388,6 @@ func createWallet(cfg *config) error {
} }
// Create the wallet. // Create the wallet.
netDir := networkDir(cfg.DataDir, activeNet.Params)
dbPath := filepath.Join(netDir, walletDbName) dbPath := filepath.Join(netDir, walletDbName)
fmt.Println("Creating the wallet...") fmt.Println("Creating the wallet...")
@ -290,6 +408,27 @@ func createWallet(cfg *config) error {
return err return err
} }
// Import the addresses in the legacy keystore to the new wallet if
// any exist.
if legacyKeyStore != nil {
fmt.Println("Importing addresses from existing wallet...")
if err := manager.Unlock([]byte(privPass)); err != nil {
return err
}
if err := convertLegacyKeystore(legacyKeyStore, manager); err != nil {
return err
}
legacyKeyStore.Lock()
legacyKeyStore = nil
// Remove the legacy key store.
if err := os.Remove(keystorePath); err != nil {
fmt.Printf("WARN: Failed to remove legacy wallet "+
"from'%s'\n", keystorePath)
}
}
manager.Close() manager.Close()
fmt.Println("The wallet has been created successfully.") fmt.Println("The wallet has been created successfully.")
return nil return nil