walletunlock.go raw
1 package gui
2
3 import (
4 "encoding/hex"
5 "encoding/json"
6 "fmt"
7 "io/ioutil"
8 "path/filepath"
9 "time"
10
11 "golang.org/x/exp/shiny/materialdesign/icons"
12 "lukechampine.com/blake3"
13
14 l "github.com/p9c/p9/pkg/gel/gio/layout"
15 "github.com/p9c/p9/pkg/gel/gio/text"
16
17 "github.com/p9c/p9/pkg/interrupt"
18
19 "github.com/p9c/p9/pkg/gel"
20 "github.com/p9c/p9/pkg/p9icons"
21 )
22
23 func (wg *WalletGUI) unlockWallet(pass string) {
24 D.Ln("entered password", pass)
25 // unlock wallet
26 // wg.cx.Config.Lock()
27 wg.cx.Config.WalletPass.Set(pass)
28 wg.cx.Config.WalletOff.F()
29 // wg.cx.Config.Unlock()
30 // load config into a fresh variable
31 // cfg := podcfgs.GetDefaultConfig()
32 var cfgFile []byte
33 var e error
34 if cfgFile, e = ioutil.ReadFile(wg.cx.Config.ConfigFile.V()); E.Chk(e) {
35 // this should not happen
36 // TODO: panic-type conditions - for gel should have a notification maybe?
37 panic("config file does not exist")
38 }
39 cfg := wg.cx.Config
40 D.Ln("loaded config")
41 if e = json.Unmarshal(cfgFile, &cfg); !E.Chk(e) {
42 D.Ln("unmarshaled config")
43 bhb := blake3.Sum256([]byte(pass))
44 bh := hex.EncodeToString(bhb[:])
45 I.Ln(pass, bh, cfg.WalletPass.V())
46 if cfg.WalletPass.V() == bh {
47 D.Ln("loading previously saved state")
48 filename := filepath.Join(wg.cx.Config.DataDir.V(), "state.json")
49 // if log.FileExists(filename) {
50 I.Ln("#### loading state data...")
51 if e = wg.State.Load(filename, wg.cx.Config.WalletPass.Bytes()); !E.Chk(e) {
52 D.Ln("#### loaded state data")
53 }
54 // it is as though it is loaded if it didn't exist
55 wg.stateLoaded.Store(true)
56 // the entered password matches the stored hash
57 wg.cx.Config.NodeOff.F()
58 wg.cx.Config.WalletOff.F()
59 wg.cx.Config.WalletPass.Set(pass)
60 if e = wg.cx.Config.WriteToFile(wg.cx.Config.ConfigFile.V()); E.Chk(e) {
61 }
62 wg.cx.Config.WalletPass.Set(pass)
63 wg.WalletWatcher = wg.Watcher()
64 // }
65 //
66 // qrText := fmt.Sprintf("parallelcoin:%s?amount=%s&message=%s",
67 // wg.State.currentReceivingAddress.Load().EncodeAddress(),
68 // wg.inputs["receiveAmount"].GetText(),
69 // wg.inputs["receiveMessage"].GetText(),
70 // )
71 // var qrc image.Image
72 // if qrc, e = qrcode.Encode(qrText, 0, qrcode.ECLevelL, 4); !E.Chk(e) {
73 // iop := paint.NewImageOp(qrc)
74 // wg.currentReceiveQRCode = &iop
75 // wg.currentReceiveQR = wg.ButtonLayout(wg.currentReceiveCopyClickable.SetClick(func() {
76 // D.Ln("clicked qr code copy clicker")
77 // if e := clipboard.WriteAll(qrText); E.Chk(e) {
78 // }
79 // })).
80 // // CornerRadius(0.5).
81 // // Corners(gel.NW | gel.SW | gel.NE).
82 // Background("white").
83 // Embed(
84 // wg.Inset(0.125,
85 // wg.Image().Src(*wg.currentReceiveQRCode).Scale(1).Fn,
86 // ).Fn,
87 // ).Fn
88 // // *wg.currentReceiveQRCode = iop
89 // }
90 }
91 } else {
92 D.Ln("failed to unlock the wallet")
93 }
94 }
95
96 func (wg *WalletGUI) getWalletUnlockAppWidget() (a *gel.App) {
97 a = wg.App(wg.Window.Width, wg.State.activePage, Break1).
98 SetMainDirection(l.Center + 1).
99 SetLogo(&p9icons.ParallelCoin).
100 SetAppTitleText("Parallelcoin Wallet")
101 wg.unlockPage = a
102 password := wg.cx.Config.WalletPass
103 exitButton := wg.WidgetPool.GetClickable()
104 unlockButton := wg.WidgetPool.GetClickable()
105 wg.unlockPassword = wg.Password(
106 "enter password", password, "DocText",
107 "DocBg", "PanelBg", func(pass string) {
108 I.Ln("wallet unlock initiated", pass)
109 wg.unlockWallet(pass)
110 },
111 )
112 // wg.unlockPage.SetThemeHook(
113 // func() {
114 // D.Ln("theme hook")
115 // // D.Ln(wg.bools)
116 // wg.cx.Config.DarkTheme.Set(*wg.Dark)
117 // b := wg.configs["config"]["DarkTheme"].Slot.(*bool)
118 // *b = *wg.Dark
119 // if wgb, ok := wg.config.Bools["DarkTheme"]; ok {
120 // wgb.Value(*wg.Dark)
121 // }
122 // var e error
123 // if e = wg.cx.Config.WriteToFile(wg.cx.Config.ConfigFile.V()); E.Chk(e) {
124 // }
125 // },
126 // )
127 a.Pages(
128 map[string]l.Widget{
129 "home": wg.Page(
130 "home", gel.Widgets{
131 gel.WidgetSize{
132 Widget:
133 func(gtx l.Context) l.Dimensions {
134 var dims l.Dimensions
135 return wg.Flex().
136 AlignMiddle().
137 Flexed(
138 1,
139 wg.VFlex().
140 Flexed(0.5, gel.EmptyMaxHeight()).
141 Rigid(
142 wg.Flex().
143 SpaceEvenly().
144 AlignMiddle().
145 Rigid(
146 wg.Fill(
147 "DocBg", l.Center, wg.TextSize.V, 0,
148 wg.Inset(
149 0.5,
150 wg.Flex().
151 AlignMiddle().
152 Rigid(
153 wg.VFlex().
154 AlignMiddle().
155 Rigid(
156 func(gtx l.Context) l.Dimensions {
157 dims = wg.Flex().
158 AlignBaseline().
159 Rigid(
160 wg.Fill(
161 "Fatal",
162 l.Center,
163 wg.TextSize.V/2,
164 0,
165 wg.Inset(
166 0.5,
167 wg.Icon().
168 Scale(gel.Scales["H3"]).
169 Color("DocBg").
170 Src(&icons.ActionLock).Fn,
171 ).Fn,
172 ).Fn,
173 ).
174 Rigid(
175 wg.Inset(
176 0.5,
177 gel.EmptySpace(0, 0),
178 ).Fn,
179 ).
180 Rigid(
181 wg.H2("locked").Color("DocText").Fn,
182 ).
183 Fn(gtx)
184 return dims
185 },
186 ).
187 Rigid(wg.Inset(0.5, gel.EmptySpace(0, 0)).Fn).
188 Rigid(
189 func(gtx l.Context) l.
190 Dimensions {
191 gtx.Constraints.Max.
192 X = dims.Size.X
193 return wg.
194 unlockPassword.
195 Fn(gtx)
196 },
197 ).
198 Rigid(wg.Inset(0.5, gel.EmptySpace(0, 0)).Fn).
199 Rigid(
200 wg.Body1(
201 fmt.Sprintf(
202 "%v idle timeout",
203 time.Duration(wg.incdecs["idleTimeout"].GetCurrent())*time.Second,
204 ),
205 ).
206 Color("DocText").
207 Font("bariol bold").
208 Fn,
209 ).
210 Rigid(
211 wg.Flex().
212 Rigid(
213 wg.Body1("Idle timeout in seconds:").Color(
214 "DocText",
215 ).Fn,
216 ).
217 Rigid(
218 wg.incdecs["idleTimeout"].
219 Color("DocText").
220 Background("DocBg").
221 Scale(gel.Scales["Caption"]).
222 Fn,
223 ).
224 Fn,
225 ).
226 Rigid(
227 wg.Flex().
228 Rigid(
229 wg.Inset(
230 0.25,
231 wg.ButtonLayout(
232 exitButton.SetClick(
233 func() {
234 interrupt.Request()
235 },
236 ),
237 ).
238 CornerRadius(0.5).
239 Corners(0).
240 Background("PanelBg").
241 Embed(
242 // wg.Fill("DocText",
243 wg.Inset(
244 0.25,
245 wg.Flex().AlignMiddle().
246 Rigid(
247 wg.Icon().
248 Scale(
249 gel.Scales["H4"],
250 ).
251 Color("DocText").
252 Src(
253 &icons.
254 MapsDirectionsRun,
255 ).Fn,
256 ).
257 Rigid(
258 wg.Inset(
259 0.5,
260 gel.EmptySpace(
261 0,
262 0,
263 ),
264 ).Fn,
265 ).
266 Rigid(
267 wg.H6("exit").Color("DocText").Fn,
268 ).
269 Rigid(
270 wg.Inset(
271 0.5,
272 gel.EmptySpace(
273 0,
274 0,
275 ),
276 ).Fn,
277 ).
278 Fn,
279 ).Fn,
280 // l.Center,
281 // wg.TextSize.True/2).Fn,
282 ).Fn,
283 ).Fn,
284 ).
285 Rigid(
286 wg.Inset(
287 0.25,
288 wg.ButtonLayout(
289 unlockButton.SetClick(
290 func() {
291 // pass := wg.unlockPassword.Editor().Text()
292 pass := wg.unlockPassword.GetPassword()
293 D.Ln(
294 ">>>>>>>>>>> unlock password",
295 pass,
296 )
297 wg.unlockWallet(pass)
298
299 },
300 ),
301 ).Background("Primary").
302 CornerRadius(0.5).
303 Corners(0).
304 Embed(
305 wg.Inset(
306 0.25,
307 wg.Flex().AlignMiddle().
308 Rigid(
309 wg.Icon().
310 Scale(gel.Scales["H4"]).
311 Color("Light").
312 Src(&icons.ActionLockOpen).Fn,
313 ).
314 Rigid(
315 wg.Inset(
316 0.5,
317 gel.EmptySpace(
318 0,
319 0,
320 ),
321 ).Fn,
322 ).
323 Rigid(
324 wg.H6("unlock").Color("Light").Fn,
325 ).
326 Rigid(
327 wg.Inset(
328 0.5,
329 gel.EmptySpace(
330 0,
331 0,
332 ),
333 ).Fn,
334 ).
335 Fn,
336 ).Fn,
337 ).Fn,
338 ).Fn,
339 ).
340 Fn,
341 ).
342 Fn,
343 ).
344 Fn,
345 ).Fn,
346 ).Fn,
347 ).
348 Fn,
349 ).Flexed(0.5, gel.EmptyMaxHeight()).Fn,
350 ).
351 Fn(gtx)
352 },
353 },
354 },
355 ),
356 "settings": wg.Page(
357 "settings", gel.Widgets{
358 gel.WidgetSize{
359 Widget: func(gtx l.Context) l.Dimensions {
360 return wg.configs.Widget(wg.config)(gtx)
361 },
362 },
363 },
364 ),
365 "console": wg.Page(
366 "console", gel.Widgets{
367 gel.WidgetSize{Widget: wg.console.Fn},
368 },
369 ),
370 "help": wg.Page(
371 "help", gel.Widgets{
372 gel.WidgetSize{Widget: wg.HelpPage()},
373 },
374 ),
375 "log": wg.Page(
376 "log", gel.Widgets{
377 gel.WidgetSize{Widget: a.Placeholder("log")},
378 },
379 ),
380 "quit": wg.Page(
381 "quit", gel.Widgets{
382 gel.WidgetSize{
383 Widget: func(gtx l.Context) l.Dimensions {
384 return wg.VFlex().
385 SpaceEvenly().
386 AlignMiddle().
387 Rigid(
388 wg.H4("are you sure?").Color(wg.unlockPage.BodyColorGet()).Alignment(text.Middle).Fn,
389 ).
390 Rigid(
391 wg.Flex().
392 // SpaceEvenly().
393 Flexed(0.5, gel.EmptyMaxWidth()).
394 Rigid(
395 wg.Button(
396 wg.clickables["quit"].SetClick(
397 func() {
398 wg.gracefulShutdown()
399 // close(wg.quit)
400 },
401 ),
402 ).Color("Light").TextScale(5).Text(
403 "yes!!!",
404 ).Fn,
405 ).
406 Flexed(0.5, gel.EmptyMaxWidth()).
407 Fn,
408 ).
409 Fn(gtx)
410 },
411 },
412 },
413 ),
414 // "goroutines": wg.Page(
415 // "log", p9.Widgets{
416 // // p9.WidgetSize{Widget: p9.EmptyMaxHeight()},
417 //
418 // p9.WidgetSize{
419 // Widget: func(gtx l.Context) l.Dimensions {
420 // le := func(gtx l.Context, index int) l.Dimensions {
421 // return wg.State.goroutines[index](gtx)
422 // }
423 // return func(gtx l.Context) l.Dimensions {
424 // return wg.ButtonInset(
425 // 0.25,
426 // wg.Fill(
427 // "DocBg",
428 // wg.lists["recent"].
429 // Vertical().
430 // // Background("DocBg").Color("DocText").Active("Primary").
431 // Length(len(wg.State.goroutines)).
432 // ListElement(le).
433 // Fn,
434 // ).Fn,
435 // ).
436 // Fn(gtx)
437 // }(gtx)
438 // // wg.NodeRunCommandChan <- "stop"
439 // // consume.Kill(wg.Worker)
440 // // consume.Kill(wg.cx.StateCfg.Miner)
441 // // close(wg.cx.NodeKill)
442 // // close(wg.cx.KillAll)
443 // // time.Sleep(time.Second*3)
444 // // interrupt.Request()
445 // // os.Exit(0)
446 // // return l.Dimensions{}
447 // },
448 // },
449 // },
450 // ),
451 "mining": wg.Page(
452 "mining", gel.Widgets{
453 gel.WidgetSize{Widget: a.Placeholder("mining")},
454 },
455 ),
456 "explorer": wg.Page(
457 "explorer", gel.Widgets{
458 gel.WidgetSize{Widget: a.Placeholder("explorer")},
459 },
460 ),
461 },
462 )
463 // a.SideBar([]l.Widget{
464 // wg.SideBarButton("overview", "overview", 0),
465 // wg.SideBarButton("send", "send", 1),
466 // wg.SideBarButton("receive", "receive", 2),
467 // wg.SideBarButton("history", "history", 3),
468 // wg.SideBarButton("explorer", "explorer", 6),
469 // wg.SideBarButton("mining", "mining", 7),
470 // wg.SideBarButton("console", "console", 9),
471 // wg.SideBarButton("settings", "settings", 5),
472 // wg.SideBarButton("log", "log", 10),
473 // wg.SideBarButton("help", "help", 8),
474 // wg.SideBarButton("quit", "quit", 11),
475 // })
476 a.ButtonBar(
477 []l.Widget{
478 // wg.PageTopBarButton(
479 // "goroutines", 0, &icons.ActionBugReport, func(name string) {
480 // wg.unlockPage.ActivePage(name)
481 // }, wg.unlockPage, "",
482 // ),
483 wg.PageTopBarButton(
484 "help", 1, &icons.ActionHelp, func(name string) {
485 wg.unlockPage.ActivePage(name)
486 }, wg.unlockPage, "",
487 ),
488 wg.PageTopBarButton(
489 "home", 4, &icons.ActionLock, func(name string) {
490 wg.unlockPage.ActivePage(name)
491 }, wg.unlockPage, "Danger",
492 ),
493 // wg.Flex().Rigid(wg.Inset(0.5, gel.EmptySpace(0, 0)).Fn).Fn,
494 // wg.PageTopBarButton(
495 // "quit", 3, &icons.ActionExitToApp, func(name string) {
496 // wg.unlockPage.ActivePage(name)
497 // }, wg.unlockPage, "",
498 // ),
499 },
500 )
501 a.StatusBar(
502 []l.Widget{
503 // wg.Inset(0.5, gel.EmptySpace(0, 0)).Fn,
504 wg.RunStatusPanel,
505 },
506 []l.Widget{
507 wg.StatusBarButton(
508 "console", 3, &p9icons.Terminal, func(name string) {
509 wg.MainApp.ActivePage(name)
510 }, a,
511 ),
512 wg.StatusBarButton(
513 "log", 4, &icons.ActionList, func(name string) {
514 D.Ln("click on button", name)
515 wg.unlockPage.ActivePage(name)
516 }, wg.unlockPage,
517 ),
518 wg.StatusBarButton(
519 "settings", 5, &icons.ActionSettings, func(name string) {
520 wg.unlockPage.ActivePage(name)
521 }, wg.unlockPage,
522 ),
523 // wg.Inset(0.5, gel.EmptySpace(0, 0)).Fn,
524 },
525 )
526 // a.PushOverlay(wg.toasts.DrawToasts())
527 // a.PushOverlay(wg.dialog.DrawDialog())
528 return
529 }
530