X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=wallet%2Frecovery_test.go;h=eff5850770d51e124075264a70d3162eb40f2ed0;hb=a8fcbab90cbbfcc0acea0a4bd1389e39ece3da56;hp=1aa034cfa7a6f518d8144c894d48b224fe9670af;hpb=08281341c2cb02ba11d4218576256688854790fc;p=bytom%2Fvapor.git diff --git a/wallet/recovery_test.go b/wallet/recovery_test.go index 1aa034cf..eff58507 100644 --- a/wallet/recovery_test.go +++ b/wallet/recovery_test.go @@ -9,14 +9,15 @@ import ( "testing" "time" - dbm "github.com/tendermint/tmlibs/db" - "github.com/vapor/account" "github.com/vapor/blockchain/pseudohsm" "github.com/vapor/blockchain/signers" "github.com/vapor/blockchain/txbuilder" + "github.com/vapor/common" "github.com/vapor/consensus" "github.com/vapor/crypto/ed25519/chainkd" + dbm "github.com/vapor/database/leveldb" + "github.com/vapor/errors" "github.com/vapor/protocol/bc" "github.com/vapor/protocol/bc/types" ) @@ -57,7 +58,7 @@ func MockSimpleUtxo(index uint64, assetID *bc.AssetID, amount uint64, ctrlProg * } func AddTxOutput(assetID bc.AssetID, amount uint64, controlProgram []byte) *types.TxOutput { - out := types.NewTxOutput(assetID, amount, controlProgram) + out := types.NewIntraChainOutput(assetID, amount, controlProgram) return out } @@ -127,6 +128,55 @@ func MockTxsP2PKH(acctMgr *account.Manager, xPub chainkd.XPub, multiTypeAccount return txs, nil } +func TestXPubsRecoveryLock(t *testing.T) { + dirPath, err := ioutil.TempDir(".", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dirPath) + + testDB := dbm.NewDB("testdb", "leveldb", dirPath) + hsm, err := pseudohsm.New(dirPath) + if err != nil { + t.Fatal(err) + } + + xpub, _, err := hsm.XCreate("test_pub", "password", "en") + if err != nil { + t.Fatal(err) + } + + acctMgr := account.NewManager(testDB, nil) + recoveryMgr := newRecoveryManager(testDB, acctMgr) + recoveryMgr.state = newRecoveryState() + recoveryMgr.state.XPubs = []chainkd.XPub{xpub.XPub} + recoveryMgr.state.XPubsStatus = newBranchRecoveryState(acctRecoveryWindow) + + recoveryMgr.state.StartTime = time.Now() + recoveryMgr.commitStatusInfo() + + if err := recoveryMgr.AcctResurrect([]chainkd.XPub{xpub.XPub}); err != nil { + t.Fatal("TestXPubsRecoveryLock err:", err) + } + + if err := recoveryMgr.AcctResurrect([]chainkd.XPub{xpub.XPub}); err != errors.Root(ErrRecoveryBusy) { + t.Fatal("TestXPubsRecoveryLock err:", err) + } + + if err := recoveryMgr.LoadStatusInfo(); err != errors.Root(ErrRecoveryBusy) { + t.Fatal("TestXPubsRecoveryLock err:", err) + } + + recoveryMgr.stopXPubsRec() + if err := recoveryMgr.LoadStatusInfo(); err != nil { + t.Fatal("TestXPubsRecoveryLock err:", err) + } + recoveryMgr.finished() + if err := recoveryMgr.AcctResurrect([]chainkd.XPub{xpub.XPub}); err != nil { + t.Fatal("TestXPubsRecoveryLock err:", err) + } +} + func TestExtendScanAddresses(t *testing.T) { dirPath, err := ioutil.TempDir(".", "") if err != nil { @@ -205,36 +255,54 @@ func TestRecoveryFromXPubs(t *testing.T) { txs, err := MockTxsP2PKH(acctMgr, xpub.XPub, false) recAcctMgr := account.NewManager(recoveryDB, nil) recoveryMgr := newRecoveryManager(recoveryDB, recAcctMgr) - if err := recoveryMgr.AcctResurrect([]chainkd.XPub{xpub.XPub}); err != nil { - t.Fatal("recovery from XPubs err:", err) - } - if err := recoveryMgr.FilterRecoveryTxs(MockBlock(txs)); err != nil { - t.Fatal("recovery from XPubs err:", err) + cases := []struct { + xPubs []chainkd.XPub + err error + }{ + {[]chainkd.XPub{xpub.XPub}, nil}, + {[]chainkd.XPub{xpub.XPub, xpub.XPub}, signers.ErrDupeXPub}, + {[]chainkd.XPub{}, signers.ErrNoXPubs}, } - Accounts, err := acctMgr.ListAccounts("") - if err != nil { - t.Fatal("recovery from XPubs err:", err) - } + for _, c := range cases { + if err := recoveryMgr.AcctResurrect(c.xPubs); errors.Root(err) != c.err { + t.Fatal("recovery from XPubs err:", err) + } - for _, acct := range Accounts { - tmp, err := recAcctMgr.GetAccountByXPubsIndex(acct.XPubs, acct.KeyIndex) if err != nil { + recoveryMgr.finished() + continue + } + if err := recoveryMgr.FilterRecoveryTxs(MockBlock(txs)); err != nil { t.Fatal("recovery from XPubs err:", err) } - if tmp == nil { - t.Fatal("accout recovery from xpubs err:", acct.KeyIndex) + Accounts, err := acctMgr.ListAccounts("") + if err != nil { + t.Fatal("recovery from XPubs err:", err) } - if acctMgr.GetBip44ContractIndex(acct.ID, true) != recAcctMgr.GetBip44ContractIndex(tmp.ID, true) { - t.Fatal("bip44 internal address index recovery from xpubs err") - } + for _, acct := range Accounts { + tmp, err := recAcctMgr.GetAccountByXPubsIndex(acct.XPubs, acct.KeyIndex) + if err != nil { + t.Fatal("recovery from XPubs err:", err) + } + + if tmp == nil { + t.Fatal("accout recovery from xpubs err:", acct.KeyIndex) + } + + if acctMgr.GetBip44ContractIndex(acct.ID, true) != recAcctMgr.GetBip44ContractIndex(tmp.ID, true) { + t.Fatal("bip44 internal address index recovery from xpubs err") + } - if acctMgr.GetBip44ContractIndex(acct.ID, false) != recAcctMgr.GetBip44ContractIndex(tmp.ID, false) { - t.Fatal("bip44 external address index recovery from xpubs err") + if acctMgr.GetBip44ContractIndex(acct.ID, false) != recAcctMgr.GetBip44ContractIndex(tmp.ID, false) { + t.Fatal("bip44 external address index recovery from xpubs err") + } } + + recoveryMgr.finished() } } @@ -276,32 +344,140 @@ func TestRecoveryByRescanAccount(t *testing.T) { } recoveryMgr := newRecoveryManager(recoveryDB, recAcctMgr) - if err := recoveryMgr.AddrResurrect(allAccounts); err != nil { - t.Fatal("recovery by rescan account err:", err) + + acct := &account.Account{ID: "testA", Alias: "test1", Signer: &signers.Signer{XPubs: []chainkd.XPub{xpub.XPub}, KeyIndex: 1, DeriveRule: 3}} + + cases := []struct { + accounts []*account.Account + err error + }{ + {allAccounts, nil}, + {[]*account.Account{acct}, signers.ErrDeriveRule}, } - recoveryMgr.FilterRecoveryTxs(MockBlock(txs)) - Accounts, err := acctMgr.ListAccounts("") - for _, acct := range Accounts { - tmp, err := recAcctMgr.GetAccountByXPubsIndex(acct.XPubs, acct.KeyIndex) + for _, c := range cases { + if err := recoveryMgr.AddrResurrect(c.accounts); errors.Root(err) != c.err { + t.Fatal("recovery by rescan account err:", err) + } + + if err != nil { + continue + } + recoveryMgr.FilterRecoveryTxs(MockBlock(txs)) + accounts, err := acctMgr.ListAccounts("") if err != nil { t.Fatal("recovery from XPubs err:", err) } - if tmp == nil { - t.Fatal("accout recovery from xpubs err:", acct.KeyIndex) - } + for _, acct := range accounts { + tmp, err := recAcctMgr.GetAccountByXPubsIndex(acct.XPubs, acct.KeyIndex) + if err != nil { + t.Fatal("recovery from XPubs err:", err) + } + + if tmp == nil { + t.Fatal("accout recovery from xpubs err:", acct.KeyIndex) + } + + if acctMgr.GetBip44ContractIndex(acct.ID, true) != recAcctMgr.GetBip44ContractIndex(tmp.ID, true) { + t.Fatal("bip44 internal address index recovery from xpubs err") + } - if acctMgr.GetBip44ContractIndex(acct.ID, true) != recAcctMgr.GetBip44ContractIndex(tmp.ID, true) { - t.Fatal("bip44 internal address index recovery from xpubs err") + if acctMgr.GetBip44ContractIndex(acct.ID, false) != recAcctMgr.GetBip44ContractIndex(tmp.ID, false) { + t.Fatal("bip44 external address index recovery from xpubs err") + } + + if acctMgr.GetContractIndex(acct.ID) != recAcctMgr.GetContractIndex(tmp.ID) { + t.Fatal("bip32 address index recovery from xpubs err") + } } + } + +} - if acctMgr.GetBip44ContractIndex(acct.ID, false) != recAcctMgr.GetBip44ContractIndex(tmp.ID, false) { - t.Fatal("bip44 external address index recovery from xpubs err") +func TestReportFound(t *testing.T) { + dirPath, err := ioutil.TempDir(".", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dirPath) + + testDB := dbm.NewDB("testdb", "leveldb", dirPath) + hsm, err := pseudohsm.New(dirPath) + if err != nil { + t.Fatal(err) + } + + xpub1, _, err := hsm.XCreate("test_pub1", "password", "en") + if err != nil { + t.Fatal(err) + } + + xpub2, _, err := hsm.XCreate("test_pub2", "password", "en") + if err != nil { + t.Fatal(err) + } + + acctMgr := account.NewManager(testDB, nil) + recoveryMgr := newRecoveryManager(testDB, acctMgr) + acc1 := &account.Account{ID: "testA", Alias: "test1", Signer: &signers.Signer{XPubs: []chainkd.XPub{xpub1.XPub}, KeyIndex: 1, DeriveRule: signers.BIP0044}} + acc2 := &account.Account{ID: "testB", Alias: "test2", Signer: &signers.Signer{XPubs: []chainkd.XPub{xpub2.XPub}, KeyIndex: 1, DeriveRule: signers.BIP0032}} + acc3 := &account.Account{ID: "testC", Alias: "test3", Signer: &signers.Signer{XPubs: []chainkd.XPub{xpub2.XPub}, KeyIndex: 2, DeriveRule: signers.BIP0044}} + + cp1 := &account.CtrlProgram{AccountID: acc1.ID, Address: "address1", KeyIndex: 10, Change: false} + cp2 := &account.CtrlProgram{AccountID: acc1.ID, Address: "address1", KeyIndex: 20, Change: true} + cp3 := &account.CtrlProgram{AccountID: acc2.ID, Address: "address1", KeyIndex: 30, Change: false} + cp4 := &account.CtrlProgram{AccountID: acc2.ID, Address: "address1", KeyIndex: 40, Change: true} + cp5 := &account.CtrlProgram{AccountID: acc3.ID, Address: "address1", KeyIndex: 50, Change: false} + cp6 := &account.CtrlProgram{AccountID: acc3.ID, Address: "address1", KeyIndex: 60, Change: true} + + if err := acctMgr.SaveAccount(acc2); err != nil { + t.Fatal("ReportFound test err:", err) + } + + if err := acctMgr.SaveAccount(acc3); err != nil { + t.Fatal("ReportFound test err:", err) + } + + recoveryMgr.state.XPubsStatus = newBranchRecoveryState(acctRecoveryWindow) + recoveryMgr.state.XPubs = []chainkd.XPub{xpub1.XPub} + recoveryMgr.state.stateForScope(acc1) + recoveryMgr.state.stateForScope(acc2) + recoveryMgr.state.stateForScope(acc3) + + cases := []struct { + acct *account.Account + cp *account.CtrlProgram + err error + status *addressRecoveryState + }{ + {acc1, cp1, nil, + &addressRecoveryState{InternalBranch: &branchRecoveryState{addrRecoveryWindow, 1, 1}, ExternalBranch: &branchRecoveryState{addrRecoveryWindow, 139, 11}, Account: acc1}}, + {acc2, cp3, nil, + &addressRecoveryState{InternalBranch: &branchRecoveryState{addrRecoveryWindow, 1, 1}, ExternalBranch: &branchRecoveryState{addrRecoveryWindow, 159, 31}, Account: acc2}}, + {acc1, cp2, nil, + &addressRecoveryState{InternalBranch: &branchRecoveryState{addrRecoveryWindow, 149, 21}, ExternalBranch: &branchRecoveryState{addrRecoveryWindow, 139, 11}, Account: acc1}}, + {acc2, cp4, nil, + &addressRecoveryState{InternalBranch: &branchRecoveryState{addrRecoveryWindow, 169, 41}, ExternalBranch: &branchRecoveryState{addrRecoveryWindow, 159, 31}, Account: acc2}}, + {acc3, cp5, nil, + &addressRecoveryState{InternalBranch: &branchRecoveryState{addrRecoveryWindow, 1, 1}, ExternalBranch: &branchRecoveryState{addrRecoveryWindow, 179, 51}, Account: acc3}}, + {acc3, cp6, nil, + &addressRecoveryState{InternalBranch: &branchRecoveryState{addrRecoveryWindow, 189, 61}, ExternalBranch: &branchRecoveryState{addrRecoveryWindow, 179, 51}, Account: acc3}}, + } + + for _, c := range cases { + if err := recoveryMgr.reportFound(c.acct, c.cp); err != c.err { + t.Fatal("ReportFound test err:", err, c.acct.ID) } - if acctMgr.GetContractIndex(acct.ID) != recAcctMgr.GetContractIndex(tmp.ID) { - t.Fatal("bip32 address index recovery from xpubs err") + status, ok := recoveryMgr.state.AccountsStatus[c.acct.ID] + if !ok { + t.Fatal("ReportFound test err: can not find status") + } + if !reflect.DeepEqual(status, c.status) { + t.Log(c.status.Account, c.status.InternalBranch, c.status.ExternalBranch) + t.Log(status.Account, status.InternalBranch, status.ExternalBranch) + t.Fatal("ReportFound test err: recovery status error") } } } @@ -334,25 +510,49 @@ func TestLoadStatusInfo(t *testing.T) { recoveryMgr.state.XPubsStatus = newBranchRecoveryState(acctRecoveryWindow) recoveryMgr.state.StartTime = time.Now() + if err := recoveryMgr.LoadStatusInfo(); err != nil { + t.Fatal("TestLoadStatusInfo err:", err) + } + recoveryMgr.commitStatusInfo() recoveryMgrRestore := newRecoveryManager(testDB, acctMgr) - recoveryMgrRestore.LoadStatusInfo() + if err := recoveryMgrRestore.LoadStatusInfo(); err != nil { + t.Fatal("TestLoadStatusInfo err:", err) + } if !reflect.DeepEqual(recoveryMgrRestore.state.XPubsStatus, recoveryMgr.state.XPubsStatus) { - t.Fatalf("testLoadStatusInfo XPubsStatus reload err") + t.Fatalf("TestLoadStatusInfo XPubsStatus reload err") } if !reflect.DeepEqual(recoveryMgrRestore.state.XPubs, recoveryMgr.state.XPubs) { - t.Fatalf("testLoadStatusInfo XPubs reload err") + t.Fatalf("TestLoadStatusInfo XPubs reload err") } if !reflect.DeepEqual(recoveryMgrRestore.state.AccountsStatus, recoveryMgr.state.AccountsStatus) { - t.Fatalf("testLoadStatusInfo AccountsStatus reload err") + t.Fatalf("TestLoadStatusInfo AccountsStatus reload err") } if !recoveryMgrRestore.state.StartTime.Equal(recoveryMgr.state.StartTime) { - t.Fatalf("testLoadStatusInfo StartTime reload err") + t.Fatalf("TestLoadStatusInfo StartTime reload err") + } + + acct := &account.Account{ID: "testA", Alias: "test1", Signer: &signers.Signer{XPubs: []chainkd.XPub{xpub.XPub}, KeyIndex: 1, DeriveRule: 3}} + recoveryMgr.state.AccountsStatus[acct.ID] = newAddressRecoveryState(addrRecoveryWindow, acct) + if err := recoveryMgr.commitStatusInfo(); err != nil { + t.Fatal("TestLoadStatusInfo err:", err) + } + if err := recoveryMgr.LoadStatusInfo(); err == nil { + t.Fatal("TestLoadStatusInfo err") + } + + recoveryMgr.state = nil + if err := recoveryMgr.commitStatusInfo(); err != nil { + t.Fatal("TestLoadStatusInfo err:", err) + } + + if err := recoveryMgr.LoadStatusInfo(); err == nil { + t.Fatal("TestLoadStatusInfo err") } } @@ -404,3 +604,91 @@ func TestStateForScope(t *testing.T) { t.Fatal("state for scope test err") } } + +func bip44ContractIndexKey(accountID string, change bool) []byte { + contractIndexPrefix := []byte("ContractIndex") + key := append(contractIndexPrefix, accountID...) + if change { + return append(key, []byte{1}...) + } + return append(key, []byte{0}...) +} + +func TestContractIndexResidue(t *testing.T) { + dirPath, err := ioutil.TempDir(".", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dirPath) + + testDB := dbm.NewDB("testdb", "leveldb", dirPath) + hsm, err := pseudohsm.New(dirPath) + if err != nil { + t.Fatal(err) + } + + xpub1, _, err := hsm.XCreate("test_pub1", "password", "en") + if err != nil { + t.Fatal(err) + } + + contractIndexResidue := uint64(5) + acctMgr := account.NewManager(testDB, nil) + recoveryMgr := newRecoveryManager(testDB, acctMgr) + acct := &account.Account{ID: "testA", Alias: "test1", Signer: &signers.Signer{XPubs: []chainkd.XPub{xpub1.XPub}, KeyIndex: 1, DeriveRule: signers.BIP0044}} + + cp1 := &account.CtrlProgram{AccountID: acct.ID, Address: "address1", KeyIndex: 10, Change: false} + + setContractIndexKey := func(acctMgr *account.Manager, accountID string, change bool) { + testDB.Set(bip44ContractIndexKey(accountID, change), common.Unit64ToBytes(contractIndexResidue)) + } + + delAccount := func(acctMgr *account.Manager, accountID string, change bool) { + acctMgr.DeleteAccount(accountID) + } + + recoveryMgr.state.XPubsStatus = newBranchRecoveryState(acctRecoveryWindow) + recoveryMgr.state.XPubs = []chainkd.XPub{xpub1.XPub} + recoveryMgr.state.stateForScope(acct) + + cases := []struct { + acct *account.Account + cp *account.CtrlProgram + preProcess func(acctMgr *account.Manager, accountID string, change bool) + err error + wantCPNum uint64 + }{ + {acct, cp1, setContractIndexKey, nil, 5}, + {acct, cp1, delAccount, nil, 10}, + } + + for _, c := range cases { + if c.preProcess != nil { + c.preProcess(acctMgr, c.acct.ID, c.cp.Change) + } + + if err := acctMgr.SaveAccount(acct); err != nil { + t.Fatal("ReportFound test err:", err) + } + + if err := recoveryMgr.reportFound(c.acct, c.cp); err != c.err { + t.Fatal("ContractIndexResidue test err:", err, c.acct.ID) + } + cps, err := acctMgr.ListControlProgram() + if err != nil { + t.Fatal("list control program err:", err) + } + + cpNum := uint64(0) + for _, cp := range cps { + if cp.Address == "" || cp.AccountID != c.acct.ID { + continue + } + cpNum++ + } + + if cpNum != c.wantCPNum { + t.Fatal("Test contract index residue cp num err want:", c.wantCPNum, " got:", cpNum) + } + } +}