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