OSDN Git Service

Small translation improvements during BM error reporting (#1693)
[bytom/bytom.git] / wallet / recovery_test.go
1 package wallet
2
3 import (
4         "fmt"
5         "io/ioutil"
6         "math/rand"
7         "os"
8         "reflect"
9         "testing"
10         "time"
11
12         "github.com/bytom/account"
13         "github.com/bytom/blockchain/pseudohsm"
14         "github.com/bytom/blockchain/signers"
15         "github.com/bytom/blockchain/txbuilder"
16         "github.com/bytom/consensus"
17         "github.com/bytom/crypto/ed25519/chainkd"
18         "github.com/bytom/errors"
19         "github.com/bytom/protocol/bc"
20         "github.com/bytom/protocol/bc/types"
21         dbm "github.com/bytom/database/leveldb"
22 )
23
24 // MockBlock mock a block
25 func MockBlock(txs []*types.Tx) *types.Block {
26         return &types.Block{
27                 BlockHeader:  types.BlockHeader{Timestamp: uint64(time.Now().Nanosecond())},
28                 Transactions: txs,
29         }
30 }
31
32 func MockSimpleUtxo(index uint64, assetID *bc.AssetID, amount uint64, ctrlProg *account.CtrlProgram) *account.UTXO {
33         if ctrlProg == nil {
34                 ctrlProg = &account.CtrlProgram{
35                         AccountID:      "",
36                         Address:        "",
37                         KeyIndex:       uint64(0),
38                         ControlProgram: []byte{81},
39                         Change:         false,
40                 }
41         }
42
43         utxo := &account.UTXO{
44                 OutputID:            bc.Hash{V0: 1},
45                 SourceID:            bc.Hash{V0: 1},
46                 AssetID:             *assetID,
47                 Amount:              amount,
48                 SourcePos:           index,
49                 ControlProgram:      ctrlProg.ControlProgram,
50                 ControlProgramIndex: ctrlProg.KeyIndex,
51                 AccountID:           ctrlProg.AccountID,
52                 Address:             ctrlProg.Address,
53                 ValidHeight:         0,
54         }
55
56         return utxo
57 }
58
59 func AddTxOutput(assetID bc.AssetID, amount uint64, controlProgram []byte) *types.TxOutput {
60         out := types.NewTxOutput(assetID, amount, controlProgram)
61         return out
62 }
63
64 func BuildTx(baseUtxo *account.UTXO, signer *signers.Signer) (*txbuilder.Template, error) {
65         tplBuilder, err := CreateTxBuilder(baseUtxo, signer)
66         if err != nil {
67                 return nil, err
68         }
69
70         tpl, _, err := tplBuilder.Build()
71         if err != nil {
72                 return nil, err
73         }
74
75         return tpl, nil
76 }
77
78 func CreateTxBuilder(baseUtxo *account.UTXO, signer *signers.Signer) (*txbuilder.TemplateBuilder, error) {
79         tplBuilder := txbuilder.NewBuilder(time.Now())
80         txOutput := AddTxOutput(baseUtxo.AssetID, 100, baseUtxo.ControlProgram)
81         tplBuilder.AddOutput(txOutput)
82         return tplBuilder, nil
83 }
84
85 func MockTxsP2PKH(acctMgr *account.Manager, xPub chainkd.XPub, multiTypeAccount bool) ([]*types.Tx, error) {
86         txs := []*types.Tx{}
87         accts := []*account.Account{}
88         for i := uint32(1); i < 32; i = i + 1 + rand.Uint32()%5 {
89                 alias := fmt.Sprintf("testAccount%d", i)
90                 deriveRule := signers.BIP0044
91                 if multiTypeAccount {
92                         deriveRule = uint8(rand.Uint32() % 2)
93                 }
94                 acct, err := account.CreateAccount([]chainkd.XPub{xPub}, 1, alias, uint64(i), deriveRule)
95                 if err != nil {
96                         return nil, err
97                 }
98
99                 if err := acctMgr.SaveAccount(acct); err != nil {
100                         return nil, err
101                 }
102
103                 accts = append(accts, acct)
104         }
105
106         for _, acct := range accts {
107                 for i := uint32(1); i < 256; i = i + 1 + rand.Uint32()%16 {
108                         controlProg, err := account.CreateCtrlProgram(acct, uint64(i), false)
109                         if err != nil {
110                                 return nil, err
111                         }
112
113                         if err := acctMgr.SaveControlPrograms(controlProg); err != nil {
114                                 return nil, err
115                         }
116
117                         utxo := MockSimpleUtxo(0, consensus.BTMAssetID, 1000000000, controlProg)
118                         tpl, err := BuildTx(utxo, acct.Signer)
119                         if err != nil {
120                                 return nil, err
121                         }
122
123                         txs = append(txs, tpl.Transaction)
124                 }
125         }
126
127         return txs, nil
128 }
129
130 func TestXPubsRecoveryLock(t *testing.T) {
131         dirPath, err := ioutil.TempDir(".", "")
132         if err != nil {
133                 t.Fatal(err)
134         }
135         defer os.RemoveAll(dirPath)
136
137         testDB := dbm.NewDB("testdb", "leveldb", dirPath)
138         hsm, err := pseudohsm.New(dirPath)
139         if err != nil {
140                 t.Fatal(err)
141         }
142
143         xpub, _, err := hsm.XCreate("test_pub", "password", "en")
144         if err != nil {
145                 t.Fatal(err)
146         }
147
148         acctMgr := account.NewManager(testDB, nil)
149         recoveryMgr := newRecoveryManager(testDB, acctMgr)
150         recoveryMgr.state = newRecoveryState()
151         recoveryMgr.state.XPubs = []chainkd.XPub{xpub.XPub}
152         recoveryMgr.state.XPubsStatus = newBranchRecoveryState(acctRecoveryWindow)
153
154         recoveryMgr.state.StartTime = time.Now()
155         recoveryMgr.commitStatusInfo()
156
157         if err := recoveryMgr.AcctResurrect([]chainkd.XPub{xpub.XPub}); err != nil {
158                 t.Fatal("TestXPubsRecoveryLock err:", err)
159         }
160
161         if err := recoveryMgr.AcctResurrect([]chainkd.XPub{xpub.XPub}); err != errors.Root(ErrRecoveryBusy) {
162                 t.Fatal("TestXPubsRecoveryLock err:", err)
163         }
164
165         if err := recoveryMgr.LoadStatusInfo(); err != errors.Root(ErrRecoveryBusy) {
166                 t.Fatal("TestXPubsRecoveryLock err:", err)
167         }
168
169         recoveryMgr.stopXPubsRec()
170         if err := recoveryMgr.LoadStatusInfo(); err != nil {
171                 t.Fatal("TestXPubsRecoveryLock err:", err)
172         }
173         recoveryMgr.finished()
174         if err := recoveryMgr.AcctResurrect([]chainkd.XPub{xpub.XPub}); err != nil {
175                 t.Fatal("TestXPubsRecoveryLock err:", err)
176         }
177 }
178
179 func TestExtendScanAddresses(t *testing.T) {
180         dirPath, err := ioutil.TempDir(".", "")
181         if err != nil {
182                 t.Fatal(err)
183         }
184         defer os.RemoveAll(dirPath)
185
186         testDB := dbm.NewDB("testdb", "leveldb", dirPath)
187         hsm, err := pseudohsm.New(dirPath)
188         if err != nil {
189                 t.Fatal(err)
190         }
191
192         xpub, _, err := hsm.XCreate("test_pub", "password", "en")
193         if err != nil {
194                 t.Fatal(err)
195         }
196
197         acctMgr := account.NewManager(testDB, nil)
198         recoveryMgr := newRecoveryManager(testDB, acctMgr)
199         acc1 := &account.Account{ID: "testA", Alias: "test1", Signer: &signers.Signer{XPubs: []chainkd.XPub{xpub.XPub}, KeyIndex: 1, DeriveRule: signers.BIP0044}}
200         acc2 := &account.Account{ID: "testB", Alias: "test2"}
201         acc3 := &account.Account{ID: "testC", Alias: "test3", Signer: &signers.Signer{XPubs: []chainkd.XPub{xpub.XPub}, KeyIndex: 2, DeriveRule: 3}}
202         acc4 := &account.Account{ID: "testD", Alias: "test4", Signer: &signers.Signer{XPubs: []chainkd.XPub{xpub.XPub}, KeyIndex: 3, DeriveRule: signers.BIP0032}}
203
204         recoveryMgr.state.stateForScope(acc1)
205         recoveryMgr.state.stateForScope(acc3)
206         recoveryMgr.state.stateForScope(acc4)
207
208         cases := []struct {
209                 acct       *account.Account
210                 err        error
211                 addressLen uint64
212         }{
213                 {acc1, nil, addrRecoveryWindow * 2},
214                 {acc2, ErrInvalidAcctID, addrRecoveryWindow * 2},
215                 {acc3, signers.ErrDeriveRule, addrRecoveryWindow * 2},
216                 {acc4, nil, addrRecoveryWindow * 3},
217         }
218
219         for _, c := range cases {
220                 if err := recoveryMgr.extendScanAddresses(c.acct.ID, true); err != c.err {
221                         t.Fatal("extend scan addresses err:", err)
222                 }
223
224                 if err := recoveryMgr.extendScanAddresses(c.acct.ID, false); err != c.err {
225                         t.Fatal("extend scan addresses err:", err)
226                 }
227
228                 if uint64(len(recoveryMgr.addresses)) != c.addressLen {
229                         t.Fatalf("extend scan addresses err: len:%d,want:%d", len(recoveryMgr.addresses), c.addressLen)
230                 }
231         }
232 }
233
234 func TestRecoveryFromXPubs(t *testing.T) {
235         dirPath, err := ioutil.TempDir(".", "")
236         if err != nil {
237                 t.Fatal(err)
238         }
239         defer os.RemoveAll(dirPath)
240
241         testDB := dbm.NewDB("testdb", "leveldb", dirPath)
242         recoveryDB := dbm.NewDB("recdb", "leveldb", dirPath)
243         hsm, err := pseudohsm.New(dirPath)
244         if err != nil {
245                 t.Fatal(err)
246         }
247
248         xpub, _, err := hsm.XCreate("test_pub", "password", "en")
249         if err != nil {
250                 t.Fatal(err)
251         }
252
253         acctMgr := account.NewManager(testDB, nil)
254         txs, err := MockTxsP2PKH(acctMgr, xpub.XPub, false)
255         recAcctMgr := account.NewManager(recoveryDB, nil)
256         recoveryMgr := newRecoveryManager(recoveryDB, recAcctMgr)
257
258         cases := []struct {
259                 xPubs []chainkd.XPub
260                 err   error
261         }{
262                 {[]chainkd.XPub{xpub.XPub}, nil},
263                 {[]chainkd.XPub{xpub.XPub, xpub.XPub}, signers.ErrDupeXPub},
264                 {[]chainkd.XPub{}, signers.ErrNoXPubs},
265         }
266
267         for _, c := range cases {
268                 if err := recoveryMgr.AcctResurrect(c.xPubs); errors.Root(err) != c.err {
269                         t.Fatal("recovery from XPubs err:", err)
270                 }
271
272                 if err != nil {
273                         recoveryMgr.finished()
274                         continue
275                 }
276                 if err := recoveryMgr.FilterRecoveryTxs(MockBlock(txs)); err != nil {
277                         t.Fatal("recovery from XPubs err:", err)
278                 }
279
280                 Accounts, err := acctMgr.ListAccounts("")
281                 if err != nil {
282                         t.Fatal("recovery from XPubs err:", err)
283                 }
284
285                 for _, acct := range Accounts {
286                         tmp, err := recAcctMgr.GetAccountByXPubsIndex(acct.XPubs, acct.KeyIndex)
287                         if err != nil {
288                                 t.Fatal("recovery from XPubs err:", err)
289                         }
290
291                         if tmp == nil {
292                                 t.Fatal("accout recovery from xpubs err:", acct.KeyIndex)
293                         }
294
295                         if acctMgr.GetBip44ContractIndex(acct.ID, true) != recAcctMgr.GetBip44ContractIndex(tmp.ID, true) {
296                                 t.Fatal("bip44 internal address index recovery from xpubs err")
297                         }
298
299                         if acctMgr.GetBip44ContractIndex(acct.ID, false) != recAcctMgr.GetBip44ContractIndex(tmp.ID, false) {
300                                 t.Fatal("bip44 external address index recovery from xpubs err")
301                         }
302                 }
303
304                 recoveryMgr.finished()
305         }
306 }
307
308 func TestRecoveryByRescanAccount(t *testing.T) {
309         dirPath, err := ioutil.TempDir(".", "")
310         if err != nil {
311                 t.Fatal(err)
312         }
313         defer os.RemoveAll(dirPath)
314
315         testDB := dbm.NewDB("testdb", "leveldb", dirPath)
316         recoveryDB := dbm.NewDB("recdb", "leveldb", dirPath)
317         hsm, err := pseudohsm.New(dirPath)
318         if err != nil {
319                 t.Fatal(err)
320         }
321
322         xpub, _, err := hsm.XCreate("test_pub", "password", "en")
323         if err != nil {
324                 t.Fatal(err)
325         }
326
327         acctMgr := account.NewManager(testDB, nil)
328         txs, err := MockTxsP2PKH(acctMgr, xpub.XPub, true)
329         if err != nil {
330                 t.Fatal("recovery by rescan account err:", err)
331         }
332
333         allAccounts, err := acctMgr.ListAccounts("")
334         if err != nil {
335                 t.Fatal("recovery by rescan account err:", err)
336         }
337
338         recAcctMgr := account.NewManager(recoveryDB, nil)
339         for _, acct := range allAccounts {
340                 if err := recAcctMgr.SaveAccount(acct); err != nil {
341                         t.Fatal("recovery by rescan account err:", err)
342                 }
343         }
344
345         recoveryMgr := newRecoveryManager(recoveryDB, recAcctMgr)
346
347         acct := &account.Account{ID: "testA", Alias: "test1", Signer: &signers.Signer{XPubs: []chainkd.XPub{xpub.XPub}, KeyIndex: 1, DeriveRule: 3}}
348
349         cases := []struct {
350                 accounts []*account.Account
351                 err      error
352         }{
353                 {allAccounts, nil},
354                 {[]*account.Account{acct}, signers.ErrDeriveRule},
355         }
356
357         for _, c := range cases {
358                 if err := recoveryMgr.AddrResurrect(c.accounts); errors.Root(err) != c.err {
359                         t.Fatal("recovery by rescan account err:", err)
360                 }
361
362                 if err != nil {
363                         continue
364                 }
365                 recoveryMgr.FilterRecoveryTxs(MockBlock(txs))
366                 accounts, err := acctMgr.ListAccounts("")
367                 if err != nil {
368                         t.Fatal("recovery from XPubs err:", err)
369                 }
370
371                 for _, acct := range accounts {
372                         tmp, err := recAcctMgr.GetAccountByXPubsIndex(acct.XPubs, acct.KeyIndex)
373                         if err != nil {
374                                 t.Fatal("recovery from XPubs err:", err)
375                         }
376
377                         if tmp == nil {
378                                 t.Fatal("accout recovery from xpubs err:", acct.KeyIndex)
379                         }
380
381                         if acctMgr.GetBip44ContractIndex(acct.ID, true) != recAcctMgr.GetBip44ContractIndex(tmp.ID, true) {
382                                 t.Fatal("bip44 internal address index recovery from xpubs err")
383                         }
384
385                         if acctMgr.GetBip44ContractIndex(acct.ID, false) != recAcctMgr.GetBip44ContractIndex(tmp.ID, false) {
386                                 t.Fatal("bip44 external address index recovery from xpubs err")
387                         }
388
389                         if acctMgr.GetContractIndex(acct.ID) != recAcctMgr.GetContractIndex(tmp.ID) {
390                                 t.Fatal("bip32 address index recovery from xpubs err")
391                         }
392                 }
393         }
394
395 }
396
397 func TestReportFound(t *testing.T) {
398         dirPath, err := ioutil.TempDir(".", "")
399         if err != nil {
400                 t.Fatal(err)
401         }
402         defer os.RemoveAll(dirPath)
403
404         testDB := dbm.NewDB("testdb", "leveldb", dirPath)
405         hsm, err := pseudohsm.New(dirPath)
406         if err != nil {
407                 t.Fatal(err)
408         }
409
410         xpub1, _, err := hsm.XCreate("test_pub1", "password", "en")
411         if err != nil {
412                 t.Fatal(err)
413         }
414
415         xpub2, _, err := hsm.XCreate("test_pub2", "password", "en")
416         if err != nil {
417                 t.Fatal(err)
418         }
419
420         acctMgr := account.NewManager(testDB, nil)
421         recoveryMgr := newRecoveryManager(testDB, acctMgr)
422         acc1 := &account.Account{ID: "testA", Alias: "test1", Signer: &signers.Signer{XPubs: []chainkd.XPub{xpub1.XPub}, KeyIndex: 1, DeriveRule: signers.BIP0044}}
423         acc2 := &account.Account{ID: "testB", Alias: "test2", Signer: &signers.Signer{XPubs: []chainkd.XPub{xpub2.XPub}, KeyIndex: 1, DeriveRule: signers.BIP0032}}
424         acc3 := &account.Account{ID: "testC", Alias: "test3", Signer: &signers.Signer{XPubs: []chainkd.XPub{xpub2.XPub}, KeyIndex: 2, DeriveRule: signers.BIP0044}}
425
426         cp1 := &account.CtrlProgram{AccountID: acc1.ID, Address: "address1", KeyIndex: 10, Change: false}
427         cp2 := &account.CtrlProgram{AccountID: acc1.ID, Address: "address1", KeyIndex: 20, Change: true}
428         cp3 := &account.CtrlProgram{AccountID: acc2.ID, Address: "address1", KeyIndex: 30, Change: false}
429         cp4 := &account.CtrlProgram{AccountID: acc2.ID, Address: "address1", KeyIndex: 40, Change: true}
430         cp5 := &account.CtrlProgram{AccountID: acc3.ID, Address: "address1", KeyIndex: 50, Change: false}
431         cp6 := &account.CtrlProgram{AccountID: acc3.ID, Address: "address1", KeyIndex: 60, Change: true}
432
433         if err := acctMgr.SaveAccount(acc2); err != nil {
434                 t.Fatal("ReportFound test err:", err)
435         }
436
437         if err := acctMgr.SaveAccount(acc3); err != nil {
438                 t.Fatal("ReportFound test err:", err)
439         }
440
441         recoveryMgr.state.XPubsStatus = newBranchRecoveryState(acctRecoveryWindow)
442         recoveryMgr.state.XPubs = []chainkd.XPub{xpub1.XPub}
443         recoveryMgr.state.stateForScope(acc1)
444         recoveryMgr.state.stateForScope(acc2)
445         recoveryMgr.state.stateForScope(acc3)
446
447         cases := []struct {
448                 acct   *account.Account
449                 cp     *account.CtrlProgram
450                 err    error
451                 status *addressRecoveryState
452         }{
453                 {acc1, cp1, nil,
454                         &addressRecoveryState{InternalBranch: &branchRecoveryState{addrRecoveryWindow, 1, 1}, ExternalBranch: &branchRecoveryState{addrRecoveryWindow, 139, 11}, Account: acc1}},
455                 {acc2, cp3, nil,
456                         &addressRecoveryState{InternalBranch: &branchRecoveryState{addrRecoveryWindow, 1, 1}, ExternalBranch: &branchRecoveryState{addrRecoveryWindow, 159, 31}, Account: acc2}},
457                 {acc1, cp2, nil,
458                         &addressRecoveryState{InternalBranch: &branchRecoveryState{addrRecoveryWindow, 149, 21}, ExternalBranch: &branchRecoveryState{addrRecoveryWindow, 139, 11}, Account: acc1}},
459                 {acc2, cp4, nil,
460                         &addressRecoveryState{InternalBranch: &branchRecoveryState{addrRecoveryWindow, 169, 41}, ExternalBranch: &branchRecoveryState{addrRecoveryWindow, 159, 31}, Account: acc2}},
461                 {acc3, cp5, nil,
462                         &addressRecoveryState{InternalBranch: &branchRecoveryState{addrRecoveryWindow, 1, 1}, ExternalBranch: &branchRecoveryState{addrRecoveryWindow, 179, 51}, Account: acc3}},
463                 {acc3, cp6, nil,
464                         &addressRecoveryState{InternalBranch: &branchRecoveryState{addrRecoveryWindow, 189, 61}, ExternalBranch: &branchRecoveryState{addrRecoveryWindow, 179, 51}, Account: acc3}},
465         }
466
467         for _, c := range cases {
468                 if err := recoveryMgr.reportFound(c.acct, c.cp); err != c.err {
469                         t.Fatal("ReportFound test err:", err, c.acct.ID)
470                 }
471
472                 status, ok := recoveryMgr.state.AccountsStatus[c.acct.ID]
473                 if !ok {
474                         t.Fatal("ReportFound test err: can not find status")
475                 }
476                 if !reflect.DeepEqual(status, c.status) {
477                         t.Log(c.status.Account, c.status.InternalBranch, c.status.ExternalBranch)
478                         t.Log(status.Account, status.InternalBranch, status.ExternalBranch)
479                         t.Fatal("ReportFound test err: recovery status error")
480                 }
481         }
482 }
483
484 func TestLoadStatusInfo(t *testing.T) {
485         dirPath, err := ioutil.TempDir(".", "")
486         if err != nil {
487                 t.Fatal(err)
488         }
489         defer os.RemoveAll(dirPath)
490
491         testDB := dbm.NewDB("testdb", "leveldb", "temp")
492         defer os.RemoveAll("temp")
493
494         hsm, err := pseudohsm.New(dirPath)
495         if err != nil {
496                 t.Fatal(err)
497         }
498
499         xpub, _, err := hsm.XCreate("test_pub", "password", "en")
500         if err != nil {
501                 t.Fatal(err)
502         }
503
504         acctMgr := account.NewManager(testDB, nil)
505         recoveryMgr := newRecoveryManager(testDB, acctMgr)
506         // StatusInit init recovery status manager.
507         recoveryMgr.state = newRecoveryState()
508         recoveryMgr.state.XPubs = []chainkd.XPub{xpub.XPub}
509         recoveryMgr.state.XPubsStatus = newBranchRecoveryState(acctRecoveryWindow)
510
511         recoveryMgr.state.StartTime = time.Now()
512         if err := recoveryMgr.LoadStatusInfo(); err != nil {
513                 t.Fatal("TestLoadStatusInfo err:", err)
514         }
515
516         recoveryMgr.commitStatusInfo()
517
518         recoveryMgrRestore := newRecoveryManager(testDB, acctMgr)
519         if err := recoveryMgrRestore.LoadStatusInfo(); err != nil {
520                 t.Fatal("TestLoadStatusInfo err:", err)
521         }
522
523         if !reflect.DeepEqual(recoveryMgrRestore.state.XPubsStatus, recoveryMgr.state.XPubsStatus) {
524                 t.Fatalf("TestLoadStatusInfo XPubsStatus reload err")
525         }
526
527         if !reflect.DeepEqual(recoveryMgrRestore.state.XPubs, recoveryMgr.state.XPubs) {
528                 t.Fatalf("TestLoadStatusInfo XPubs reload err")
529         }
530
531         if !reflect.DeepEqual(recoveryMgrRestore.state.AccountsStatus, recoveryMgr.state.AccountsStatus) {
532                 t.Fatalf("TestLoadStatusInfo AccountsStatus reload err")
533         }
534
535         if !recoveryMgrRestore.state.StartTime.Equal(recoveryMgr.state.StartTime) {
536                 t.Fatalf("TestLoadStatusInfo StartTime reload err")
537         }
538
539         acct := &account.Account{ID: "testA", Alias: "test1", Signer: &signers.Signer{XPubs: []chainkd.XPub{xpub.XPub}, KeyIndex: 1, DeriveRule: 3}}
540         recoveryMgr.state.AccountsStatus[acct.ID] = newAddressRecoveryState(addrRecoveryWindow, acct)
541         if err := recoveryMgr.commitStatusInfo(); err != nil {
542                 t.Fatal("TestLoadStatusInfo err:", err)
543         }
544         if err := recoveryMgr.LoadStatusInfo(); err == nil {
545                 t.Fatal("TestLoadStatusInfo err")
546         }
547
548         recoveryMgr.state = nil
549         if err := recoveryMgr.commitStatusInfo(); err != nil {
550                 t.Fatal("TestLoadStatusInfo err:", err)
551         }
552
553         if err := recoveryMgr.LoadStatusInfo(); err == nil {
554                 t.Fatal("TestLoadStatusInfo err")
555         }
556 }
557
558 func TestLock(t *testing.T) {
559         dirPath, err := ioutil.TempDir(".", "")
560         if err != nil {
561                 t.Fatal(err)
562         }
563         defer os.RemoveAll(dirPath)
564
565         testDB := dbm.NewDB("testdb", "leveldb", "temp")
566         defer os.RemoveAll("temp")
567
568         acctMgr := account.NewManager(testDB, nil)
569         recoveryMgr := newRecoveryManager(testDB, acctMgr)
570         if !recoveryMgr.tryStartXPubsRec() {
571                 t.Fatal("recovery manager try lock test err")
572         }
573
574         if recoveryMgr.tryStartXPubsRec() {
575                 t.Fatal("recovery manager relock test err")
576         }
577
578         recoveryMgr.stopXPubsRec()
579
580         if !recoveryMgr.tryStartXPubsRec() {
581                 t.Fatal("recovery manager try lock test err")
582         }
583 }
584
585 func TestStateForScope(t *testing.T) {
586         state := newRecoveryState()
587         acc1 := &account.Account{ID: "test1", Alias: "testA"}
588         state.stateForScope(acc1)
589         if !reflect.DeepEqual(state.AccountsStatus[acc1.ID].Account, acc1) {
590                 t.Fatal("state for scope test err")
591         }
592
593         acc2 := &account.Account{ID: "test1", Alias: "testB"}
594         state.stateForScope(acc2)
595
596         if reflect.DeepEqual(state.AccountsStatus[acc2.ID].Account, acc2) {
597                 t.Fatal("state for scope test err")
598         }
599
600         acc3 := &account.Account{ID: "test2", Alias: "testC"}
601         state.stateForScope(acc3)
602         if !reflect.DeepEqual(state.AccountsStatus[acc3.ID].Account, acc3) {
603                 t.Fatal("state for scope test err")
604         }
605 }