12 "github.com/vapor/account"
13 acc "github.com/vapor/account"
14 "github.com/vapor/blockchain/pseudohsm"
15 "github.com/vapor/blockchain/signers"
16 "github.com/vapor/blockchain/txbuilder"
17 "github.com/vapor/common"
18 "github.com/vapor/consensus"
19 "github.com/vapor/crypto/ed25519/chainkd"
20 dbm "github.com/vapor/database/leveldb"
21 "github.com/vapor/errors"
22 "github.com/vapor/protocol/bc"
23 "github.com/vapor/protocol/bc/types"
26 // MockBlock mock a block
27 func MockBlock(txs []*types.Tx) *types.Block {
29 BlockHeader: types.BlockHeader{Timestamp: uint64(time.Now().Nanosecond())},
34 func MockSimpleUtxo(index uint64, assetID *bc.AssetID, amount uint64, ctrlProg *account.CtrlProgram) *account.UTXO {
36 ctrlProg = &account.CtrlProgram{
40 ControlProgram: []byte{81},
45 utxo := &account.UTXO{
46 OutputID: bc.Hash{V0: 1},
47 SourceID: bc.Hash{V0: 1},
51 ControlProgram: ctrlProg.ControlProgram,
52 ControlProgramIndex: ctrlProg.KeyIndex,
53 AccountID: ctrlProg.AccountID,
54 Address: ctrlProg.Address,
61 func AddInput(sourceID bc.Hash, assetID bc.AssetID, amount uint64, pos uint64, controlProgram []byte) *types.TxInput {
62 return types.NewSpendInput(nil, sourceID, assetID, amount, pos, controlProgram)
65 func AddTxOutput(assetID bc.AssetID, amount uint64, controlProgram []byte) *types.TxOutput {
66 return types.NewIntraChainOutput(assetID, amount, controlProgram)
69 func BuildTx(baseUtxo *account.UTXO, signer *signers.Signer) (*txbuilder.Template, error) {
70 tplBuilder, err := CreateTxBuilder(baseUtxo, signer)
75 tpl, _, err := tplBuilder.Build()
83 func CreateTxBuilder(baseUtxo *account.UTXO, signer *signers.Signer) (*txbuilder.TemplateBuilder, error) {
84 tplBuilder := txbuilder.NewBuilder(time.Now())
85 txInput := AddInput(bc.Hash{V0: 1}, baseUtxo.AssetID, 10000, uint64(1), baseUtxo.ControlProgram)
86 txOutput := AddTxOutput(baseUtxo.AssetID, 100, baseUtxo.ControlProgram)
87 tplBuilder.AddInput(txInput, &txbuilder.SigningInstruction{Position: 0})
88 tplBuilder.AddOutput(txOutput)
89 return tplBuilder, nil
92 func MockTxsP2PKH(acctMgr *account.Manager, xPub chainkd.XPub, multiTypeAccount bool) ([]*types.Tx, error) {
94 accts := []*account.Account{}
95 for i := uint32(1); i < 32; i = i + 1 + rand.Uint32()%5 {
96 alias := fmt.Sprintf("testAccount%d", i)
97 deriveRule := signers.BIP0044
99 deriveRule = uint8(rand.Uint32() % 2)
101 acct, err := account.CreateAccount([]chainkd.XPub{xPub}, 1, alias, uint64(i), deriveRule)
106 if err := acctMgr.SaveAccount(acct); err != nil {
110 accts = append(accts, acct)
113 for _, acct := range accts {
114 for i := uint32(1); i < 256; i = i + 1 + rand.Uint32()%16 {
115 controlProg, err := account.CreateCtrlProgram(acct, uint64(i), false)
120 if err := acctMgr.SaveControlPrograms(controlProg); err != nil {
124 utxo := MockSimpleUtxo(0, consensus.BTMAssetID, 1000000000, controlProg)
125 tpl, err := BuildTx(utxo, acct.Signer)
130 txs = append(txs, tpl.Transaction)
137 func TestXPubsRecoveryLock(t *testing.T) {
138 dirPath, err := ioutil.TempDir(".", "")
142 defer os.RemoveAll(dirPath)
144 testDB := dbm.NewDB("testdb", "leveldb", dirPath)
145 walletStore := NewMockWalletStore(testDB)
146 hsm, err := pseudohsm.New(dirPath)
151 xpub, _, err := hsm.XCreate("test_pub", "password", "en")
156 acctStore := NewMockAccountStore(testDB)
157 acctMgr := account.NewManager(acctStore, nil)
158 recoveryMgr := NewRecoveryManager(walletStore, acctMgr)
159 recoveryMgr.state = newRecoveryState()
160 recoveryMgr.state.XPubs = []chainkd.XPub{xpub.XPub}
161 recoveryMgr.state.XPubsStatus = newBranchRecoveryState(acctRecoveryWindow)
163 recoveryMgr.state.StartTime = time.Now()
164 recoveryMgr.commitStatusInfo()
166 if err := recoveryMgr.AcctResurrect([]chainkd.XPub{xpub.XPub}); err != nil {
167 t.Fatal("TestXPubsRecoveryLock err:", err)
170 if err := recoveryMgr.AcctResurrect([]chainkd.XPub{xpub.XPub}); err != errors.Root(ErrRecoveryBusy) {
171 t.Fatal("TestXPubsRecoveryLock err:", err)
174 if err := recoveryMgr.LoadStatusInfo(); err != errors.Root(ErrRecoveryBusy) {
175 t.Fatal("TestXPubsRecoveryLock err:", err)
178 recoveryMgr.stopXPubsRec()
179 if err := recoveryMgr.LoadStatusInfo(); err != nil {
180 t.Fatal("TestXPubsRecoveryLock err:", err)
182 recoveryMgr.finished()
183 if err := recoveryMgr.AcctResurrect([]chainkd.XPub{xpub.XPub}); err != nil {
184 t.Fatal("TestXPubsRecoveryLock err:", err)
188 func TestExtendScanAddresses(t *testing.T) {
189 dirPath, err := ioutil.TempDir(".", "")
193 defer os.RemoveAll(dirPath)
195 testDB := dbm.NewDB("testdb", "leveldb", dirPath)
196 walletStore := NewMockWalletStore(testDB)
197 hsm, err := pseudohsm.New(dirPath)
202 xpub, _, err := hsm.XCreate("test_pub", "password", "en")
207 acctStore := NewMockAccountStore(testDB)
208 acctMgr := account.NewManager(acctStore, nil)
209 recoveryMgr := NewRecoveryManager(walletStore, acctMgr)
210 acc1 := &account.Account{ID: "testA", Alias: "test1", Signer: &signers.Signer{XPubs: []chainkd.XPub{xpub.XPub}, KeyIndex: 1, DeriveRule: signers.BIP0044}}
211 acc2 := &account.Account{ID: "testB", Alias: "test2"}
212 acc3 := &account.Account{ID: "testC", Alias: "test3", Signer: &signers.Signer{XPubs: []chainkd.XPub{xpub.XPub}, KeyIndex: 2, DeriveRule: 3}}
213 acc4 := &account.Account{ID: "testD", Alias: "test4", Signer: &signers.Signer{XPubs: []chainkd.XPub{xpub.XPub}, KeyIndex: 3, DeriveRule: signers.BIP0032}}
215 recoveryMgr.state.stateForScope(acc1)
216 recoveryMgr.state.stateForScope(acc3)
217 recoveryMgr.state.stateForScope(acc4)
220 acct *account.Account
224 {acc1, nil, addrRecoveryWindow * 2},
225 {acc2, ErrInvalidAcctID, addrRecoveryWindow * 2},
226 {acc3, signers.ErrDeriveRule, addrRecoveryWindow * 2},
227 {acc4, nil, addrRecoveryWindow * 3},
230 for _, c := range cases {
231 if err := recoveryMgr.extendScanAddresses(c.acct.ID, true); err != c.err {
232 t.Fatal("extend scan addresses err:", err)
235 if err := recoveryMgr.extendScanAddresses(c.acct.ID, false); err != c.err {
236 t.Fatal("extend scan addresses err:", err)
239 if uint64(len(recoveryMgr.addresses)) != c.addressLen {
240 t.Fatalf("extend scan addresses err: len:%d,want:%d", len(recoveryMgr.addresses), c.addressLen)
245 func TestRecoveryFromXPubs(t *testing.T) {
246 dirPath, err := ioutil.TempDir(".", "")
250 defer os.RemoveAll(dirPath)
252 testDB := dbm.NewDB("testdb", "leveldb", dirPath)
253 recoveryDB := dbm.NewDB("recdb", "leveldb", dirPath)
254 recoveryStore := NewMockWalletStore(recoveryDB)
255 hsm, err := pseudohsm.New(dirPath)
260 xpub, _, err := hsm.XCreate("test_pub", "password", "en")
265 acctStore := NewMockAccountStore(testDB)
266 acctMgr := account.NewManager(acctStore, nil)
267 txs, err := MockTxsP2PKH(acctMgr, xpub.XPub, false)
268 recActStore := NewMockAccountStore(recoveryDB)
269 recAcctMgr := account.NewManager(recActStore, nil)
270 recoveryMgr := NewRecoveryManager(recoveryStore, recAcctMgr)
276 {[]chainkd.XPub{xpub.XPub}, nil},
277 {[]chainkd.XPub{xpub.XPub, xpub.XPub}, signers.ErrDupeXPub},
278 {[]chainkd.XPub{}, signers.ErrNoXPubs},
281 for _, c := range cases {
282 if err := recoveryMgr.AcctResurrect(c.xPubs); errors.Root(err) != c.err {
283 t.Fatal("recovery from XPubs err:", err)
287 recoveryMgr.finished()
290 if err := recoveryMgr.FilterRecoveryTxs(MockBlock(txs)); err != nil {
291 t.Fatal("recovery from XPubs err:", err)
294 Accounts, err := acctMgr.ListAccounts("")
296 t.Fatal("recovery from XPubs err:", err)
299 for _, acct := range Accounts {
300 tmp, err := recAcctMgr.GetAccountByXPubsIndex(acct.XPubs, acct.KeyIndex)
301 if err != nil && err != acc.ErrFindAccount {
302 t.Fatal("recovery from XPubs err:", err)
306 t.Fatal("accout recovery from xpubs err:", acct.KeyIndex)
309 if acctMgr.GetBip44ContractIndex(acct.ID, true) != recAcctMgr.GetBip44ContractIndex(tmp.ID, true) {
310 t.Fatal("bip44 internal address index recovery from xpubs err")
313 if acctMgr.GetBip44ContractIndex(acct.ID, false) != recAcctMgr.GetBip44ContractIndex(tmp.ID, false) {
314 t.Fatal("bip44 external address index recovery from xpubs err")
318 recoveryMgr.finished()
322 func TestRecoveryByRescanAccount(t *testing.T) {
323 dirPath, err := ioutil.TempDir(".", "")
327 defer os.RemoveAll(dirPath)
329 testDB := dbm.NewDB("testdb", "leveldb", dirPath)
330 recoveryDB := dbm.NewDB("recdb", "leveldb", dirPath)
331 recoveryStore := NewMockWalletStore(recoveryDB)
332 hsm, err := pseudohsm.New(dirPath)
337 xpub, _, err := hsm.XCreate("test_pub", "password", "en")
342 acctStore := NewMockAccountStore(testDB)
343 acctMgr := account.NewManager(acctStore, nil)
344 txs, err := MockTxsP2PKH(acctMgr, xpub.XPub, true)
346 t.Fatal("recovery by rescan account err:", err)
349 allAccounts, err := acctMgr.ListAccounts("")
351 t.Fatal("recovery by rescan account err:", err)
354 recActStore := NewMockAccountStore(recoveryDB)
355 recAcctMgr := account.NewManager(recActStore, nil)
356 for _, acct := range allAccounts {
357 if err := recAcctMgr.SaveAccount(acct); err != nil {
358 t.Fatal("recovery by rescan account err:", err)
362 recoveryMgr := NewRecoveryManager(recoveryStore, recAcctMgr)
364 acct := &account.Account{ID: "testA", Alias: "test1", Signer: &signers.Signer{XPubs: []chainkd.XPub{xpub.XPub}, KeyIndex: 1, DeriveRule: 3}}
367 accounts []*account.Account
371 {[]*account.Account{acct}, signers.ErrDeriveRule},
374 for _, c := range cases {
375 if err := recoveryMgr.AddrResurrect(c.accounts); errors.Root(err) != c.err {
376 t.Fatal("recovery by rescan account err:", err)
382 recoveryMgr.FilterRecoveryTxs(MockBlock(txs))
383 accounts, err := acctMgr.ListAccounts("")
385 t.Fatal("recovery from XPubs err:", err)
388 for _, acct := range accounts {
389 tmp, err := recAcctMgr.GetAccountByXPubsIndex(acct.XPubs, acct.KeyIndex)
390 if err != nil && err != acc.ErrFindAccount {
391 t.Fatal("recovery from XPubs err:", err)
395 t.Fatal("accout recovery from xpubs err:", acct.KeyIndex)
398 if acctMgr.GetBip44ContractIndex(acct.ID, true) != recAcctMgr.GetBip44ContractIndex(tmp.ID, true) {
399 t.Fatal("bip44 internal address index recovery from xpubs err")
402 if acctMgr.GetBip44ContractIndex(acct.ID, false) != recAcctMgr.GetBip44ContractIndex(tmp.ID, false) {
403 t.Fatal("bip44 external address index recovery from xpubs err")
406 if acctMgr.GetContractIndex(acct.ID) != recAcctMgr.GetContractIndex(tmp.ID) {
407 t.Fatal("bip32 address index recovery from xpubs err")
414 func TestReportFound(t *testing.T) {
415 dirPath, err := ioutil.TempDir(".", "")
419 defer os.RemoveAll(dirPath)
421 testDB := dbm.NewDB("testdb", "leveldb", dirPath)
422 testStore := NewMockWalletStore(testDB)
423 hsm, err := pseudohsm.New(dirPath)
428 xpub1, _, err := hsm.XCreate("test_pub1", "password", "en")
433 xpub2, _, err := hsm.XCreate("test_pub2", "password", "en")
438 acctStore := NewMockAccountStore(testDB)
439 acctMgr := account.NewManager(acctStore, nil)
440 recoveryMgr := NewRecoveryManager(testStore, acctMgr)
441 acc1 := &account.Account{ID: "testA", Alias: "test1", Signer: &signers.Signer{XPubs: []chainkd.XPub{xpub1.XPub}, KeyIndex: 1, DeriveRule: signers.BIP0044}}
442 acc2 := &account.Account{ID: "testB", Alias: "test2", Signer: &signers.Signer{XPubs: []chainkd.XPub{xpub2.XPub}, KeyIndex: 1, DeriveRule: signers.BIP0032}}
443 acc3 := &account.Account{ID: "testC", Alias: "test3", Signer: &signers.Signer{XPubs: []chainkd.XPub{xpub2.XPub}, KeyIndex: 2, DeriveRule: signers.BIP0044}}
445 cp1 := &account.CtrlProgram{AccountID: acc1.ID, Address: "address1", KeyIndex: 10, Change: false}
446 cp2 := &account.CtrlProgram{AccountID: acc1.ID, Address: "address1", KeyIndex: 20, Change: true}
447 cp3 := &account.CtrlProgram{AccountID: acc2.ID, Address: "address1", KeyIndex: 30, Change: false}
448 cp4 := &account.CtrlProgram{AccountID: acc2.ID, Address: "address1", KeyIndex: 40, Change: true}
449 cp5 := &account.CtrlProgram{AccountID: acc3.ID, Address: "address1", KeyIndex: 50, Change: false}
450 cp6 := &account.CtrlProgram{AccountID: acc3.ID, Address: "address1", KeyIndex: 60, Change: true}
452 if err := acctMgr.SaveAccount(acc2); err != nil {
453 t.Fatal("ReportFound test err:", err)
456 if err := acctMgr.SaveAccount(acc3); err != nil {
457 t.Fatal("ReportFound test err:", err)
460 recoveryMgr.state.XPubsStatus = newBranchRecoveryState(acctRecoveryWindow)
461 recoveryMgr.state.XPubs = []chainkd.XPub{xpub1.XPub}
462 recoveryMgr.state.stateForScope(acc1)
463 recoveryMgr.state.stateForScope(acc2)
464 recoveryMgr.state.stateForScope(acc3)
467 acct *account.Account
468 cp *account.CtrlProgram
470 status *addressRecoveryState
473 &addressRecoveryState{InternalBranch: &branchRecoveryState{addrRecoveryWindow, 1, 1}, ExternalBranch: &branchRecoveryState{addrRecoveryWindow, 139, 11}, Account: acc1}},
475 &addressRecoveryState{InternalBranch: &branchRecoveryState{addrRecoveryWindow, 1, 1}, ExternalBranch: &branchRecoveryState{addrRecoveryWindow, 159, 31}, Account: acc2}},
477 &addressRecoveryState{InternalBranch: &branchRecoveryState{addrRecoveryWindow, 149, 21}, ExternalBranch: &branchRecoveryState{addrRecoveryWindow, 139, 11}, Account: acc1}},
479 &addressRecoveryState{InternalBranch: &branchRecoveryState{addrRecoveryWindow, 169, 41}, ExternalBranch: &branchRecoveryState{addrRecoveryWindow, 159, 31}, Account: acc2}},
481 &addressRecoveryState{InternalBranch: &branchRecoveryState{addrRecoveryWindow, 1, 1}, ExternalBranch: &branchRecoveryState{addrRecoveryWindow, 179, 51}, Account: acc3}},
483 &addressRecoveryState{InternalBranch: &branchRecoveryState{addrRecoveryWindow, 189, 61}, ExternalBranch: &branchRecoveryState{addrRecoveryWindow, 179, 51}, Account: acc3}},
486 for _, c := range cases {
487 if err := recoveryMgr.reportFound(c.acct, c.cp); err != c.err {
488 t.Fatal("ReportFound test err:", err, c.acct.ID)
491 status, ok := recoveryMgr.state.AccountsStatus[c.acct.ID]
493 t.Fatal("ReportFound test err: can not find status")
495 if !reflect.DeepEqual(status, c.status) {
496 t.Log(c.status.Account, c.status.InternalBranch, c.status.ExternalBranch)
497 t.Log(status.Account, status.InternalBranch, status.ExternalBranch)
498 t.Fatal("ReportFound test err: recovery status error")
503 func TestLoadStatusInfo(t *testing.T) {
504 dirPath, err := ioutil.TempDir(".", "")
508 defer os.RemoveAll(dirPath)
510 testDB := dbm.NewDB("testdb", "leveldb", "temp")
511 testStore := NewMockWalletStore(testDB)
512 defer os.RemoveAll("temp")
514 hsm, err := pseudohsm.New(dirPath)
519 xpub, _, err := hsm.XCreate("test_pub", "password", "en")
524 acctStore := NewMockAccountStore(testDB)
525 acctMgr := account.NewManager(acctStore, nil)
526 recoveryMgr := NewRecoveryManager(testStore, acctMgr)
527 // StatusInit init recovery status manager.
528 recoveryMgr.state = newRecoveryState()
529 recoveryMgr.state.XPubs = []chainkd.XPub{xpub.XPub}
530 recoveryMgr.state.XPubsStatus = newBranchRecoveryState(acctRecoveryWindow)
532 recoveryMgr.state.StartTime = time.Now()
533 if err := recoveryMgr.LoadStatusInfo(); err != nil {
534 t.Fatal("TestLoadStatusInfo err:", err)
537 recoveryMgr.commitStatusInfo()
539 recoveryMgrRestore := NewRecoveryManager(testStore, acctMgr)
540 if err := recoveryMgrRestore.LoadStatusInfo(); err != nil {
541 t.Fatal("TestLoadStatusInfo err:", err)
544 if !reflect.DeepEqual(recoveryMgrRestore.state.XPubsStatus, recoveryMgr.state.XPubsStatus) {
545 t.Fatalf("TestLoadStatusInfo XPubsStatus reload err")
548 if !reflect.DeepEqual(recoveryMgrRestore.state.XPubs, recoveryMgr.state.XPubs) {
549 t.Fatalf("TestLoadStatusInfo XPubs reload err")
552 if !reflect.DeepEqual(recoveryMgrRestore.state.AccountsStatus, recoveryMgr.state.AccountsStatus) {
553 t.Fatalf("TestLoadStatusInfo AccountsStatus reload err")
556 if !recoveryMgrRestore.state.StartTime.Equal(recoveryMgr.state.StartTime) {
557 t.Fatalf("TestLoadStatusInfo StartTime reload err")
560 acct := &account.Account{ID: "testA", Alias: "test1", Signer: &signers.Signer{XPubs: []chainkd.XPub{xpub.XPub}, KeyIndex: 1, DeriveRule: 3}}
561 recoveryMgr.state.AccountsStatus[acct.ID] = newAddressRecoveryState(addrRecoveryWindow, acct)
562 if err := recoveryMgr.commitStatusInfo(); err != nil {
563 t.Fatal("TestLoadStatusInfo err:", err)
565 if err := recoveryMgr.LoadStatusInfo(); err == nil {
566 t.Fatal("TestLoadStatusInfo err")
569 recoveryMgr.state = nil
570 if err := recoveryMgr.commitStatusInfo(); err != nil {
571 t.Fatal("TestLoadStatusInfo err:", err)
574 if err := recoveryMgr.LoadStatusInfo(); err == nil {
575 t.Fatal("TestLoadStatusInfo err")
579 func TestLock(t *testing.T) {
580 dirPath, err := ioutil.TempDir(".", "")
584 defer os.RemoveAll(dirPath)
586 testDB := dbm.NewDB("testdb", "leveldb", "temp")
587 testStore := NewMockWalletStore(testDB)
588 defer os.RemoveAll("temp")
590 acctStore := NewMockAccountStore(testDB)
591 acctMgr := account.NewManager(acctStore, nil)
592 recoveryMgr := NewRecoveryManager(testStore, acctMgr)
593 if !recoveryMgr.tryStartXPubsRec() {
594 t.Fatal("recovery manager try lock test err")
597 if recoveryMgr.tryStartXPubsRec() {
598 t.Fatal("recovery manager relock test err")
601 recoveryMgr.stopXPubsRec()
603 if !recoveryMgr.tryStartXPubsRec() {
604 t.Fatal("recovery manager try lock test err")
608 func TestStateForScope(t *testing.T) {
609 state := newRecoveryState()
610 acc1 := &account.Account{ID: "test1", Alias: "testA"}
611 state.stateForScope(acc1)
612 if !reflect.DeepEqual(state.AccountsStatus[acc1.ID].Account, acc1) {
613 t.Fatal("state for scope test err")
616 acc2 := &account.Account{ID: "test1", Alias: "testB"}
617 state.stateForScope(acc2)
619 if reflect.DeepEqual(state.AccountsStatus[acc2.ID].Account, acc2) {
620 t.Fatal("state for scope test err")
623 acc3 := &account.Account{ID: "test2", Alias: "testC"}
624 state.stateForScope(acc3)
625 if !reflect.DeepEqual(state.AccountsStatus[acc3.ID].Account, acc3) {
626 t.Fatal("state for scope test err")
630 func TestContractIndexResidue(t *testing.T) {
631 dirPath, err := ioutil.TempDir(".", "")
635 defer os.RemoveAll(dirPath)
637 testDB := dbm.NewDB("testdb", "leveldb", dirPath)
638 testStore := NewMockWalletStore(testDB)
639 hsm, err := pseudohsm.New(dirPath)
644 xpub1, _, err := hsm.XCreate("test_pub1", "password", "en")
649 contractIndexResidue := uint64(5)
650 acctStore := NewMockAccountStore(testDB)
651 acctMgr := account.NewManager(acctStore, nil)
652 recoveryMgr := NewRecoveryManager(testStore, acctMgr)
653 acct := &account.Account{ID: "testA", Alias: "test1", Signer: &signers.Signer{XPubs: []chainkd.XPub{xpub1.XPub}, KeyIndex: 1, DeriveRule: signers.BIP0044}}
655 cp1 := &account.CtrlProgram{AccountID: acct.ID, Address: "address1", KeyIndex: 10, Change: false}
657 setContractIndexKey := func(acctMgr *account.Manager, accountID string, change bool) {
658 testDB.Set(Bip44ContractIndexKey(accountID, change), common.Unit64ToBytes(contractIndexResidue))
661 delAccount := func(acctMgr *account.Manager, accountID string, change bool) {
662 acctMgr.DeleteAccount(accountID)
665 recoveryMgr.state.XPubsStatus = newBranchRecoveryState(acctRecoveryWindow)
666 recoveryMgr.state.XPubs = []chainkd.XPub{xpub1.XPub}
667 recoveryMgr.state.stateForScope(acct)
670 acct *account.Account
671 cp *account.CtrlProgram
672 preProcess func(acctMgr *account.Manager, accountID string, change bool)
676 {acct, cp1, setContractIndexKey, nil, 5},
677 {acct, cp1, delAccount, nil, 10},
680 for _, c := range cases {
681 if c.preProcess != nil {
682 c.preProcess(acctMgr, c.acct.ID, c.cp.Change)
685 if err := acctMgr.SaveAccount(acct); err != nil {
686 t.Fatal("ReportFound test err:", err)
689 if err := recoveryMgr.reportFound(c.acct, c.cp); err != c.err {
690 t.Fatal("ContractIndexResidue test err:", err, c.acct.ID)
692 cps, err := acctMgr.ListControlProgram()
694 t.Fatal("list control program err:", err)
698 for _, cp := range cps {
699 if cp.Address == "" || cp.AccountID != c.acct.ID {
705 if cpNum != c.wantCPNum {
706 t.Fatal("Test contract index residue cp num err want:", c.wantCPNum, " got:", cpNum)