walletsetup.go raw

   1  package wallet
   2  
   3  import (
   4  	"bufio"
   5  	"os"
   6  	"path/filepath"
   7  	"time"
   8  
   9  	"github.com/p9c/p9/pkg/chaincfg"
  10  	"github.com/p9c/p9/pkg/constant"
  11  	"github.com/p9c/p9/pkg/util"
  12  	"github.com/p9c/p9/pkg/util/legacy/keystore"
  13  	"github.com/p9c/p9/pkg/util/prompt"
  14  	"github.com/p9c/p9/pkg/waddrmgr"
  15  	"github.com/p9c/p9/pkg/walletdb"
  16  	"github.com/p9c/p9/pkg/wire"
  17  	"github.com/p9c/p9/pod/config"
  18  	// This initializes the bdb driver
  19  	_ "github.com/p9c/p9/pkg/walletdb/bdb"
  20  )
  21  
  22  // CreateSimulationWallet is intended to be called from the rpcclient and used
  23  // to create a wallet for actors involved in simulations.
  24  func CreateSimulationWallet(activenet *chaincfg.Params, cfg *config.Config) (e error) {
  25  	// Simulation wallet password is 'password'.
  26  	privPass := []byte("password")
  27  	// Public passphrase is the default.
  28  	pubPass := []byte(InsecurePubPassphrase)
  29  	netDir := NetworkDir(cfg.DataDir.V(), activenet)
  30  	// Create the wallet.
  31  	dbPath := filepath.Join(netDir, constant.WalletDbName)
  32  	I.Ln("Creating the wallet...")
  33  	// Create the wallet database backed by bolt db.
  34  	db, e := walletdb.Create("bdb", dbPath)
  35  	if e != nil {
  36  		return e
  37  	}
  38  	defer func() {
  39  		if e = db.Close(); E.Chk(e) {
  40  		}
  41  	}()
  42  	// Create the wallet.
  43  	e = Create(db, pubPass, privPass, nil, activenet, time.Now())
  44  	if e != nil {
  45  		return e
  46  	}
  47  	I.Ln("The wallet has been created successfully.")
  48  	return nil
  49  }
  50  
  51  // CreateWallet prompts the user for information needed to generate a new wallet and generates the wallet accordingly.
  52  // The new wallet will reside at the provided path.
  53  func CreateWallet(activenet *chaincfg.Params, config *config.Config) (e error) {
  54  	dbDir := *config.WalletFile
  55  	loader := NewLoader(activenet, dbDir.V(), 250)
  56  	D.Ln("WalletPage", loader.ChainParams.Name)
  57  	// When there is a legacy keystore, open it now to ensure any errors don't end up exiting the process after the user
  58  	// has spent time entering a bunch of information.
  59  	netDir := NetworkDir(config.DataDir.V(), activenet)
  60  	keystorePath := filepath.Join(netDir, keystore.Filename)
  61  	var legacyKeyStore *keystore.Store
  62  	_, e = os.Stat(keystorePath)
  63  	if e != nil && !os.IsNotExist(e) {
  64  		// A stat error not due to a non-existant file should be returned to the caller.
  65  		return e
  66  	} else if e == nil {
  67  		// Keystore file exists.
  68  		legacyKeyStore, e = keystore.OpenDir(netDir)
  69  		if e != nil {
  70  			return e
  71  		}
  72  	}
  73  	// Start by prompting for the private passphrase. When there is an existing keystore, the user will be promped for
  74  	// that passphrase, otherwise they will be prompted for a new one.
  75  	reader := bufio.NewReader(os.Stdin)
  76  	privPass, e := prompt.PrivatePass(reader, legacyKeyStore)
  77  	if e != nil {
  78  		D.Ln(e)
  79  		time.Sleep(time.Second * 3)
  80  		return e
  81  	}
  82  	// When there exists a legacy keystore, unlock it now and set up a callback to import all keystore keys into the new
  83  	// walletdb wallet
  84  	if legacyKeyStore != nil {
  85  		e = legacyKeyStore.Unlock(privPass)
  86  		if e != nil {
  87  			return e
  88  		}
  89  		// Import the addresses in the legacy keystore to the new wallet if any exist, locking each wallet again when
  90  		// finished.
  91  		loader.RunAfterLoad(
  92  			func(w *Wallet) {
  93  				defer func() {
  94  					e = legacyKeyStore.Lock()
  95  					if e != nil {
  96  						D.Ln(e)
  97  					}
  98  				}()
  99  				I.Ln("Importing addresses from existing wallet...")
 100  				lockChan := make(chan time.Time, 1)
 101  				defer func() {
 102  					lockChan <- time.Time{}
 103  				}()
 104  				e = w.Unlock(privPass, lockChan)
 105  				if e != nil {
 106  					E.F(
 107  						"ERR: Failed to unlock new wallet "+
 108  							"during old wallet key import: %v", e,
 109  					)
 110  					return
 111  				}
 112  				e = convertLegacyKeystore(legacyKeyStore, w)
 113  				if e != nil {
 114  					E.F(
 115  						"ERR: Failed to import keys from old "+
 116  							"wallet format: %v %s", e,
 117  					)
 118  					return
 119  				}
 120  				// Remove the legacy key store.
 121  				e = os.Remove(keystorePath)
 122  				if e != nil {
 123  					E.Ln(
 124  						"WARN: Failed to remove legacy wallet "+
 125  							"from'%s'\n", keystorePath,
 126  					)
 127  				}
 128  			},
 129  		)
 130  	}
 131  	// Ascertain the public passphrase. This will either be a value specified by the user or the default hard-coded
 132  	// public passphrase if the user does not want the additional public data encryption.
 133  	var pubPass []byte
 134  	if pubPass, e = prompt.PublicPass(reader, privPass, []byte(""),
 135  		config.WalletPass.Bytes());E.Chk(e){
 136  		time.Sleep(time.Second * 5)
 137  		return e
 138  	}
 139  	// Ascertain the wallet generation seed. This will either be an automatically generated value the user has already
 140  	// confirmed or a value the user has entered which has already been validated.
 141  	seed, e := prompt.Seed(reader)
 142  	if e != nil {
 143  		D.Ln(e)
 144  		time.Sleep(time.Second * 5)
 145  		return e
 146  	}
 147  	D.Ln("Creating the wallet")
 148  	w, e := loader.CreateNewWallet(pubPass, privPass, seed, time.Now(), false, config, nil)
 149  	if e != nil {
 150  		D.Ln(e)
 151  		time.Sleep(time.Second * 5)
 152  		return e
 153  	}
 154  	w.Manager.Close()
 155  	D.Ln("The wallet has been created successfully.")
 156  	return nil
 157  }
 158  
 159  // NetworkDir returns the directory name of a network directory to hold wallet files.
 160  func NetworkDir(dataDir string, chainParams *chaincfg.Params) string {
 161  	netname := chainParams.Name
 162  	// For now, we must always name the testnet data directory as "testnet" and not "testnet3" or any other version, as
 163  	// the chaincfg testnet3 paramaters will likely be switched to being named "testnet3" in the future. This is done to
 164  	// future proof that change, and an upgrade plan to move the testnet3 data directory can be worked out later.
 165  	if chainParams.Net == wire.TestNet3 {
 166  		netname = "testnet"
 167  	}
 168  	return filepath.Join(dataDir, netname)
 169  }
 170  
 171  // // checkCreateDir checks that the path exists and is a directory.
 172  // // If path does not exist, it is created.
 173  // func checkCreateDir(// 	path string) (e error) {
 174  // 	if fi, e := os.Stat(path); E.Chk(e) {
 175  // 		if os.IsNotExist(e) {
 176  // 			// Attempt data directory creation
 177  // 			if e = os.MkdirAll(path, 0700); E.Chk(e) {
 178  // 				return fmt.Errorf("cannot create directory: %s", e)
 179  // 			}
 180  // 		} else {
 181  // 			return fmt.Errorf("error checking directory: %s", e)
 182  // 		}
 183  // 	} else {
 184  // 		if !fi.IsDir() {
 185  // 			return fmt.Errorf("path '%s' is not a directory", path)
 186  // 		}
 187  // 	}
 188  // 	return nil
 189  // }
 190  
 191  // convertLegacyKeystore converts all of the addresses in the passed legacy key store to the new waddrmgr.Manager
 192  // format. Both the legacy keystore and the new manager must be unlocked.
 193  func convertLegacyKeystore(legacyKeyStore *keystore.Store, w *Wallet) (e error) {
 194  	netParams := legacyKeyStore.Net()
 195  	blockStamp := waddrmgr.BlockStamp{
 196  		Height: 0,
 197  		Hash:   *netParams.GenesisHash,
 198  	}
 199  	for _, walletAddr := range legacyKeyStore.ActiveAddresses() {
 200  		switch addr := walletAddr.(type) {
 201  		case keystore.PubKeyAddress:
 202  			privKey, e := addr.PrivKey()
 203  			if e != nil {
 204  				W.F(
 205  					"Failed to obtain private key "+
 206  						"for address %v: %v", addr.Address(),
 207  					e,
 208  				)
 209  				continue
 210  			}
 211  			wif, e := util.NewWIF(
 212  				privKey,
 213  				netParams, addr.Compressed(),
 214  			)
 215  			if e != nil {
 216  				E.Ln(
 217  					"Failed to create wallet "+
 218  						"import format for address %v: %v",
 219  					addr.Address(), e,
 220  				)
 221  				continue
 222  			}
 223  			_, e = w.ImportPrivateKey(
 224  				waddrmgr.KeyScopeBIP0044,
 225  				wif, &blockStamp, false,
 226  			)
 227  			if e != nil {
 228  				W.F(
 229  					"WARN: Failed to import private "+
 230  						"key for address %v: %v",
 231  					addr.Address(), e,
 232  				)
 233  				continue
 234  			}
 235  		case keystore.ScriptAddress:
 236  			_, e := w.ImportP2SHRedeemScript(addr.Script())
 237  			if e != nil {
 238  				W.F(
 239  					"WARN: Failed to import "+
 240  						"pay-to-script-hash script for "+
 241  						"address %v: %v\n", addr.Address(), e,
 242  				)
 243  				continue
 244  			}
 245  		default:
 246  			W.F(
 247  				"WARN: Skipping unrecognized legacy "+
 248  					"keystore type: %T\n", addr,
 249  			)
 250  			continue
 251  		}
 252  	}
 253  	return nil
 254  }
 255