OSDN Git Service

Merge branch 'master' into wallet-store-interface
[bytom/vapor.git] / wallet / wallet_test.go
1 package wallet
2
3 import (
4         "encoding/binary"
5         "encoding/json"
6         "fmt"
7         "io/ioutil"
8         "os"
9         "sort"
10         "strings"
11         "testing"
12         "time"
13
14         "github.com/vapor/account"
15         acc "github.com/vapor/account"
16         "github.com/vapor/asset"
17         "github.com/vapor/blockchain/query"
18         "github.com/vapor/blockchain/signers"
19         "github.com/vapor/blockchain/txbuilder"
20         "github.com/vapor/common"
21         "github.com/vapor/config"
22         "github.com/vapor/consensus"
23         "github.com/vapor/crypto/ed25519/chainkd"
24         "github.com/vapor/crypto/sha3pool"
25         dbm "github.com/vapor/database/leveldb"
26         "github.com/vapor/database/storage"
27         "github.com/vapor/errors"
28         "github.com/vapor/event"
29         "github.com/vapor/protocol"
30         "github.com/vapor/protocol/bc"
31         "github.com/vapor/protocol/bc/types"
32         "github.com/vapor/protocol/state"
33 )
34
35 func TestEncodeDecodeGlobalTxIndex(t *testing.T) {
36         want := &struct {
37                 BlockHash bc.Hash
38                 Position  uint64
39         }{
40                 BlockHash: bc.NewHash([32]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20}),
41                 Position:  1,
42         }
43
44         globalTxIdx := CalcGlobalTxIndex(&want.BlockHash, want.Position)
45         blockHashGot, positionGot := parseGlobalTxIdx(globalTxIdx)
46         if *blockHashGot != want.BlockHash {
47                 t.Errorf("blockHash mismatch. Get: %v. Expect: %v", *blockHashGot, want.BlockHash)
48         }
49
50         if positionGot != want.Position {
51                 t.Errorf("position mismatch. Get: %v. Expect: %v", positionGot, want.Position)
52         }
53 }
54
55 func TestWalletVersion(t *testing.T) {
56         // prepare wallet
57         dirPath, err := ioutil.TempDir(".", "")
58         if err != nil {
59                 t.Fatal(err)
60         }
61         defer os.RemoveAll(dirPath)
62
63         testDB := dbm.NewDB("testdb", "leveldb", "temp")
64         walletStore := NewMockWalletStore(testDB)
65         defer func() {
66                 testDB.Close()
67                 os.RemoveAll("temp")
68         }()
69
70         dispatcher := event.NewDispatcher()
71         w := mockWallet(walletStore, nil, nil, nil, dispatcher, false)
72
73         walletStatus := new(StatusInfo)
74         if err := w.store.SetWalletInfo(walletStatus); err != nil {
75                 t.Fatal(err)
76         }
77
78         status, err := w.store.GetWalletInfo()
79         if err != nil {
80                 t.Fatal(err)
81         }
82         w.Status = *status
83
84         if err := w.checkWalletInfo(); err != errWalletVersionMismatch {
85                 t.Fatal("fail to detect legacy wallet version")
86         }
87
88         // lower wallet version test case
89         lowerVersion := StatusInfo{Version: currentVersion - 1}
90         if err := w.store.SetWalletInfo(&lowerVersion); err != nil {
91                 t.Fatal(err)
92         }
93
94         status, err = w.store.GetWalletInfo()
95         if err != nil {
96                 t.Fatal(err)
97         }
98         w.Status = *status
99
100         if err := w.checkWalletInfo(); err != errWalletVersionMismatch {
101                 t.Fatal("fail to detect expired wallet version")
102         }
103 }
104
105 func mockUTXO(controlProg *account.CtrlProgram, assetID *bc.AssetID) *account.UTXO {
106         utxo := &account.UTXO{}
107         utxo.OutputID = bc.Hash{V0: 1}
108         utxo.SourceID = bc.Hash{V0: 2}
109         utxo.AssetID = *assetID
110         utxo.Amount = 1000000000
111         utxo.SourcePos = 0
112         utxo.ControlProgram = controlProg.ControlProgram
113         utxo.AccountID = controlProg.AccountID
114         utxo.Address = controlProg.Address
115         utxo.ControlProgramIndex = controlProg.KeyIndex
116         return utxo
117 }
118
119 func mockTxData(utxos []*account.UTXO, testAccount *account.Account) (*txbuilder.Template, *types.TxData, error) {
120         tplBuilder := txbuilder.NewBuilder(time.Now())
121
122         for _, utxo := range utxos {
123                 txInput, sigInst, err := account.UtxoToInputs(testAccount.Signer, utxo)
124                 if err != nil {
125                         return nil, nil, err
126                 }
127                 tplBuilder.AddInput(txInput, sigInst)
128
129                 out := &types.TxOutput{}
130                 if utxo.AssetID == *consensus.BTMAssetID {
131                         out = types.NewIntraChainOutput(utxo.AssetID, 100, utxo.ControlProgram)
132                 } else {
133                         out = types.NewIntraChainOutput(utxo.AssetID, utxo.Amount, utxo.ControlProgram)
134                 }
135                 tplBuilder.AddOutput(out)
136         }
137
138         return tplBuilder.Build()
139 }
140
141 func mockWallet(store WalletStore, account *account.Manager, asset *asset.Registry, chain *protocol.Chain, dispatcher *event.Dispatcher, txIndexFlag bool) *Wallet {
142         wallet := &Wallet{
143                 store:           store,
144                 AccountMgr:      account,
145                 AssetReg:        asset,
146                 chain:           chain,
147                 RecoveryMgr:     newRecoveryManager(store, account),
148                 EventDispatcher: dispatcher,
149                 TxIndexFlag:     txIndexFlag,
150         }
151         wallet.txMsgSub, _ = wallet.EventDispatcher.Subscribe(protocol.TxMsgEvent{})
152         return wallet
153 }
154
155 func mockSingleBlock(tx *types.Tx) *types.Block {
156         return &types.Block{
157                 BlockHeader: types.BlockHeader{
158                         Version: 1,
159                         Height:  1,
160                 },
161                 Transactions: []*types.Tx{config.GenesisTx(), tx},
162         }
163 }
164
165 type mockStore struct {
166         db dbm.DB
167         // cache cache
168 }
169
170 func newMockStore(db dbm.DB) *mockStore {
171         return &mockStore{
172                 db: db,
173         }
174 }
175
176 func (s *mockStore) BlockExist(hash *bc.Hash) bool                                { return false }
177 func (s *mockStore) GetBlock(*bc.Hash) (*types.Block, error)                      { return nil, nil }
178 func (s *mockStore) GetBlockHeader(*bc.Hash) (*types.BlockHeader, error)          { return nil, nil }
179 func (s *mockStore) GetStoreStatus() *protocol.BlockStoreState                    { return nil }
180 func (s *mockStore) GetTransactionStatus(*bc.Hash) (*bc.TransactionStatus, error) { return nil, nil }
181 func (s *mockStore) GetTransactionsUtxo(*state.UtxoViewpoint, []*bc.Tx) error     { return nil }
182 func (s *mockStore) GetUtxo(*bc.Hash) (*storage.UtxoEntry, error)                 { return nil, nil }
183 func (s *mockStore) GetVoteResult(uint64) (*state.VoteResult, error)              { return nil, nil }
184 func (s *mockStore) GetMainChainHash(uint64) (*bc.Hash, error)                    { return nil, nil }
185 func (s *mockStore) GetBlockHashesByHeight(uint64) ([]*bc.Hash, error)            { return nil, nil }
186 func (s *mockStore) SaveBlock(*types.Block, *bc.TransactionStatus) error          { return nil }
187 func (s *mockStore) SaveBlockHeader(*types.BlockHeader) error                     { return nil }
188 func (s *mockStore) SaveChainStatus(*types.BlockHeader, *types.BlockHeader, []*types.BlockHeader, *state.UtxoViewpoint, []*state.VoteResult) error {
189         return nil
190 }
191
192 type mStore struct {
193         blockHeaders map[bc.Hash]*types.BlockHeader
194 }
195
196 func (s *mStore) BlockExist(hash *bc.Hash) bool           { return false }
197 func (s *mStore) GetBlock(*bc.Hash) (*types.Block, error) { return nil, nil }
198 func (s *mStore) GetBlockHeader(hash *bc.Hash) (*types.BlockHeader, error) {
199         return s.blockHeaders[*hash], nil
200 }
201 func (s *mStore) GetStoreStatus() *protocol.BlockStoreState                    { return nil }
202 func (s *mStore) GetTransactionStatus(*bc.Hash) (*bc.TransactionStatus, error) { return nil, nil }
203 func (s *mStore) GetTransactionsUtxo(*state.UtxoViewpoint, []*bc.Tx) error     { return nil }
204 func (s *mStore) GetUtxo(*bc.Hash) (*storage.UtxoEntry, error)                 { return nil, nil }
205 func (s *mStore) GetVoteResult(uint64) (*state.VoteResult, error)              { return nil, nil }
206 func (s *mStore) GetMainChainHash(uint64) (*bc.Hash, error)                    { return nil, nil }
207 func (s *mStore) GetBlockHashesByHeight(uint64) ([]*bc.Hash, error)            { return nil, nil }
208 func (s *mStore) SaveBlock(*types.Block, *bc.TransactionStatus) error          { return nil }
209 func (s *mStore) SaveBlockHeader(blockHeader *types.BlockHeader) error {
210         s.blockHeaders[blockHeader.Hash()] = blockHeader
211         return nil
212 }
213 func (s *mStore) SaveChainStatus(*types.BlockHeader, *types.BlockHeader, []*types.BlockHeader, *state.UtxoViewpoint, []*state.VoteResult) error {
214         return nil
215 }
216
217 //---------------------
218
219 var (
220         ContractPrefix = "Contract:"
221 )
222
223 // errors
224 var (
225         // ErrFindAccount        = errors.New("Failed to find account")
226         errAccntTxIDNotFound = errors.New("account TXID not found")
227         errGetAsset          = errors.New("Failed to find asset definition")
228 )
229
230 func accountIndexKey(xpubs []chainkd.XPub) []byte {
231         var hash [32]byte
232         var xPubs []byte
233         cpy := append([]chainkd.XPub{}, xpubs[:]...)
234         sort.Sort(signers.SortKeys(cpy))
235         for _, xpub := range cpy {
236                 xPubs = append(xPubs, xpub[:]...)
237         }
238         sha3pool.Sum256(hash[:], xPubs)
239         return append([]byte(dbm.AccountIndexPrefix), hash[:]...)
240 }
241
242 func Bip44ContractIndexKey(accountID string, change bool) []byte {
243         key := append([]byte(dbm.ContractIndexPrefix), accountID...)
244         if change {
245                 return append(key, []byte{1}...)
246         }
247         return append(key, []byte{0}...)
248 }
249
250 // ContractKey account control promgram store prefix
251 func ContractKey(hash bc.Hash) []byte {
252         return append([]byte(ContractPrefix), hash.Bytes()...)
253 }
254
255 // AccountIDKey account id store prefix
256 func AccountIDKey(accountID string) []byte {
257         return append([]byte(dbm.AccountPrefix), []byte(accountID)...)
258 }
259
260 // StandardUTXOKey makes an account unspent outputs key to store
261 func StandardUTXOKey(id bc.Hash) []byte {
262         return append(dbm.UTXOPrefix, id.Bytes()...)
263 }
264
265 // ContractUTXOKey makes a smart contract unspent outputs key to store
266 func ContractUTXOKey(id bc.Hash) []byte {
267         return append(dbm.SUTXOPrefix, id.Bytes()...)
268 }
269
270 func calcDeleteKey(blockHeight uint64) []byte {
271         return []byte(fmt.Sprintf("%s%016x", dbm.TxPrefix, blockHeight))
272 }
273
274 func calcTxIndexKey(txID string) []byte {
275         return append(dbm.TxIndexPrefix, []byte(txID)...)
276 }
277
278 func calcAnnotatedKey(formatKey string) []byte {
279         return append(dbm.TxPrefix, []byte(formatKey)...)
280 }
281
282 func calcUnconfirmedTxKey(formatKey string) []byte {
283         return append(dbm.UnconfirmedTxPrefix, []byte(formatKey)...)
284 }
285
286 func CalcGlobalTxIndexKey(txID string) []byte {
287         return append(dbm.GlobalTxIndexPrefix, []byte(txID)...)
288 }
289
290 func CalcGlobalTxIndex(blockHash *bc.Hash, position uint64) []byte {
291         txIdx := make([]byte, 40)
292         copy(txIdx[:32], blockHash.Bytes())
293         binary.BigEndian.PutUint64(txIdx[32:], position)
294         return txIdx
295 }
296
297 func formatKey(blockHeight uint64, position uint32) string {
298         return fmt.Sprintf("%016x%08x", blockHeight, position)
299 }
300
301 func contractIndexKey(accountID string) []byte {
302         return append([]byte(dbm.ContractIndexPrefix), []byte(accountID)...)
303 }
304
305 func accountAliasKey(name string) []byte {
306         return append([]byte(dbm.AccountAliasPrefix), []byte(name)...)
307 }
308
309 // MockWalletStore store wallet using leveldb
310 type MockWalletStore struct {
311         walletDB dbm.DB
312         batch    dbm.Batch
313 }
314
315 // NewMockWalletStore create new MockWalletStore struct
316 func NewMockWalletStore(db dbm.DB) *MockWalletStore {
317         return &MockWalletStore{
318                 walletDB: db,
319                 batch:    nil,
320         }
321 }
322
323 // InitBatch initial batch
324 func (store *MockWalletStore) InitBatch() error {
325         if store.batch != nil {
326                 return errors.New("MockWalletStore initail fail, store batch is not nil.")
327         }
328         store.batch = store.walletDB.NewBatch()
329         return nil
330 }
331
332 // CommitBatch commit batch
333 func (store *MockWalletStore) CommitBatch() error {
334         if store.batch == nil {
335                 return errors.New("MockWalletStore commit fail, store batch is nil.")
336         }
337         store.batch.Write()
338         store.batch = nil
339         return nil
340 }
341
342 // DeleteContractUTXO delete contract utxo by outputID
343 func (store *MockWalletStore) DeleteContractUTXO(outputID bc.Hash) {
344         if store.batch == nil {
345                 store.walletDB.Delete(ContractUTXOKey(outputID))
346         } else {
347                 store.batch.Delete(ContractUTXOKey(outputID))
348         }
349 }
350
351 // DeleteRecoveryStatus delete recovery status
352 func (store *MockWalletStore) DeleteRecoveryStatus() {
353         if store.batch == nil {
354                 store.walletDB.Delete(dbm.RecoveryKey)
355         } else {
356                 store.batch.Delete(dbm.RecoveryKey)
357         }
358 }
359
360 // DeleteTransactions delete transactions when orphan block rollback
361 func (store *MockWalletStore) DeleteTransactions(height uint64) {
362         batch := store.walletDB.NewBatch()
363         if store.batch != nil {
364                 batch = store.batch
365         }
366         txIter := store.walletDB.IteratorPrefix(calcDeleteKey(height))
367         defer txIter.Release()
368
369         tmpTx := query.AnnotatedTx{}
370         for txIter.Next() {
371                 if err := json.Unmarshal(txIter.Value(), &tmpTx); err == nil {
372                         batch.Delete(calcTxIndexKey(tmpTx.ID.String()))
373                 }
374                 batch.Delete(txIter.Key())
375         }
376         if store.batch == nil {
377                 batch.Write()
378         }
379 }
380
381 // DeleteUnconfirmedTransaction delete unconfirmed tx by txID
382 func (store *MockWalletStore) DeleteUnconfirmedTransaction(txID string) {
383         if store.batch == nil {
384                 store.walletDB.Delete(calcUnconfirmedTxKey(txID))
385         } else {
386                 store.batch.Delete(calcUnconfirmedTxKey(txID))
387         }
388 }
389
390 // DeleteWalletTransactions delete all txs in wallet
391 func (store *MockWalletStore) DeleteWalletTransactions() {
392         batch := store.walletDB.NewBatch()
393         if store.batch != nil {
394                 batch = store.batch
395         }
396         txIter := store.walletDB.IteratorPrefix([]byte(dbm.TxPrefix))
397         defer txIter.Release()
398
399         for txIter.Next() {
400                 batch.Delete(txIter.Key())
401         }
402
403         txIndexIter := store.walletDB.IteratorPrefix([]byte(dbm.TxIndexPrefix))
404         defer txIndexIter.Release()
405
406         for txIndexIter.Next() {
407                 batch.Delete(txIndexIter.Key())
408         }
409         if store.batch == nil {
410                 batch.Write()
411         }
412 }
413
414 // DeleteWalletUTXOs delete all txs in wallet
415 func (store *MockWalletStore) DeleteWalletUTXOs() {
416         batch := store.walletDB.NewBatch()
417         if store.batch != nil {
418                 batch = store.batch
419         }
420         ruIter := store.walletDB.IteratorPrefix([]byte(dbm.UTXOPrefix))
421         defer ruIter.Release()
422         for ruIter.Next() {
423                 batch.Delete(ruIter.Key())
424         }
425
426         suIter := store.walletDB.IteratorPrefix([]byte(dbm.SUTXOPrefix))
427         defer suIter.Release()
428         for suIter.Next() {
429                 batch.Delete(suIter.Key())
430         }
431         if store.batch == nil {
432                 batch.Write()
433         }
434 }
435
436 // GetAsset get asset by assetID
437 func (store *MockWalletStore) GetAsset(assetID *bc.AssetID) (*asset.Asset, error) {
438         definitionByte := store.walletDB.Get(asset.ExtAssetKey(assetID))
439         if definitionByte == nil {
440                 return nil, errGetAsset
441         }
442         definitionMap := make(map[string]interface{})
443         if err := json.Unmarshal(definitionByte, &definitionMap); err != nil {
444                 return nil, err
445         }
446         alias := assetID.String()
447         externalAsset := &asset.Asset{
448                 AssetID:           *assetID,
449                 Alias:             &alias,
450                 DefinitionMap:     definitionMap,
451                 RawDefinitionByte: definitionByte,
452         }
453         return externalAsset, nil
454 }
455
456 // GetControlProgram get raw program by hash
457 func (store *MockWalletStore) GetControlProgram(hash bc.Hash) (*acc.CtrlProgram, error) {
458         rawProgram := store.walletDB.Get(ContractKey(hash))
459         if rawProgram == nil {
460                 return nil, acc.ErrFindCtrlProgram
461         }
462         accountCP := new(acc.CtrlProgram)
463         if err := json.Unmarshal(rawProgram, &accountCP); err != nil {
464                 return nil, err
465         }
466         return accountCP, nil
467 }
468
469 // GetGlobalTransactionIndex get global tx by txID
470 func (store *MockWalletStore) GetGlobalTransactionIndex(txID string) []byte {
471         return store.walletDB.Get(CalcGlobalTxIndexKey(txID))
472 }
473
474 // GetStandardUTXO get standard utxo by id
475 func (store *MockWalletStore) GetStandardUTXO(outid bc.Hash) (*acc.UTXO, error) {
476         rawUTXO := store.walletDB.Get(StandardUTXOKey(outid))
477         if rawUTXO == nil {
478                 return nil, fmt.Errorf("failed get standard UTXO, outputID: %s ", outid.String())
479         }
480         UTXO := new(acc.UTXO)
481         if err := json.Unmarshal(rawUTXO, UTXO); err != nil {
482                 return nil, err
483         }
484         return UTXO, nil
485 }
486
487 // GetTransaction get tx by txid
488 func (store *MockWalletStore) GetTransaction(txID string) (*query.AnnotatedTx, error) {
489         formatKey := store.walletDB.Get(calcTxIndexKey(txID))
490         if formatKey == nil {
491                 return nil, errAccntTxIDNotFound
492         }
493         rawTx := store.walletDB.Get(calcAnnotatedKey(string(formatKey)))
494         tx := new(query.AnnotatedTx)
495         if err := json.Unmarshal(rawTx, tx); err != nil {
496                 return nil, err
497         }
498         return tx, nil
499 }
500
501 // GetUnconfirmedTransaction get unconfirmed tx by txID
502 func (store *MockWalletStore) GetUnconfirmedTransaction(txID string) (*query.AnnotatedTx, error) {
503         rawUnconfirmedTx := store.walletDB.Get(calcUnconfirmedTxKey(txID))
504         if rawUnconfirmedTx == nil {
505                 return nil, fmt.Errorf("failed get unconfirmed tx, txID: %s ", txID)
506         }
507         tx := new(query.AnnotatedTx)
508         if err := json.Unmarshal(rawUnconfirmedTx, tx); err != nil {
509                 return nil, err
510         }
511         return tx, nil
512 }
513
514 // GetRecoveryStatus delete recovery status
515 func (store *MockWalletStore) GetRecoveryStatus() (*RecoveryState, error) {
516         rawStatus := store.walletDB.Get(dbm.RecoveryKey)
517         if rawStatus == nil {
518                 return nil, ErrGetRecoveryStatus
519         }
520         state := new(RecoveryState)
521         if err := json.Unmarshal(rawStatus, state); err != nil {
522                 return nil, err
523         }
524         return state, nil
525 }
526
527 // GetWalletInfo get wallet information
528 func (store *MockWalletStore) GetWalletInfo() (*StatusInfo, error) {
529         rawStatus := store.walletDB.Get([]byte(dbm.WalletKey))
530         if rawStatus == nil {
531                 return nil, fmt.Errorf("failed get wallet info")
532         }
533         status := new(StatusInfo)
534         if err := json.Unmarshal(rawStatus, status); err != nil {
535                 return nil, err
536         }
537         return status, nil
538 }
539
540 // ListAccountUTXOs get all account unspent outputs
541 func (store *MockWalletStore) ListAccountUTXOs(key string) ([]*acc.UTXO, error) {
542         accountUtxoIter := store.walletDB.IteratorPrefix([]byte(key))
543         defer accountUtxoIter.Release()
544
545         confirmedUTXOs := []*acc.UTXO{}
546         for accountUtxoIter.Next() {
547                 utxo := new(acc.UTXO)
548                 if err := json.Unmarshal(accountUtxoIter.Value(), utxo); err != nil {
549                         return nil, err
550                 }
551                 confirmedUTXOs = append(confirmedUTXOs, utxo)
552         }
553         return confirmedUTXOs, nil
554 }
555
556 func (store *MockWalletStore) ListTransactions(accountID string, StartTxID string, count uint, unconfirmed bool) ([]*query.AnnotatedTx, error) {
557         annotatedTxs := []*query.AnnotatedTx{}
558         var startKey []byte
559         preFix := dbm.TxPrefix
560
561         if StartTxID != "" {
562                 if unconfirmed {
563                         startKey = calcUnconfirmedTxKey(StartTxID)
564                 } else {
565                         formatKey := store.walletDB.Get(calcTxIndexKey(StartTxID))
566                         if formatKey == nil {
567                                 return nil, errAccntTxIDNotFound
568                         }
569                         startKey = calcAnnotatedKey(string(formatKey))
570                 }
571         }
572
573         if unconfirmed {
574                 preFix = dbm.UnconfirmedTxPrefix
575         }
576
577         itr := store.walletDB.IteratorPrefixWithStart([]byte(preFix), startKey, true)
578         defer itr.Release()
579
580         for txNum := count; itr.Next() && txNum > 0; txNum-- {
581                 annotatedTx := new(query.AnnotatedTx)
582                 if err := json.Unmarshal(itr.Value(), &annotatedTx); err != nil {
583                         return nil, err
584                 }
585                 annotatedTxs = append(annotatedTxs, annotatedTx)
586         }
587
588         return annotatedTxs, nil
589 }
590
591 // ListUnconfirmedTransactions get all unconfirmed txs
592 func (store *MockWalletStore) ListUnconfirmedTransactions() ([]*query.AnnotatedTx, error) {
593         annotatedTxs := []*query.AnnotatedTx{}
594         txIter := store.walletDB.IteratorPrefix([]byte(dbm.UnconfirmedTxPrefix))
595         defer txIter.Release()
596
597         for txIter.Next() {
598                 annotatedTx := &query.AnnotatedTx{}
599                 if err := json.Unmarshal(txIter.Value(), &annotatedTx); err != nil {
600                         return nil, err
601                 }
602                 annotatedTxs = append(annotatedTxs, annotatedTx)
603         }
604         return annotatedTxs, nil
605 }
606
607 // SetAssetDefinition set assetID and definition
608 func (store *MockWalletStore) SetAssetDefinition(assetID *bc.AssetID, definition []byte) {
609         if store.batch == nil {
610                 store.walletDB.Set(asset.ExtAssetKey(assetID), definition)
611         } else {
612                 store.batch.Set(asset.ExtAssetKey(assetID), definition)
613         }
614 }
615
616 // SetContractUTXO set standard utxo
617 func (store *MockWalletStore) SetContractUTXO(outputID bc.Hash, utxo *acc.UTXO) error {
618         data, err := json.Marshal(utxo)
619         if err != nil {
620                 return err
621         }
622         if store.batch == nil {
623                 store.walletDB.Set(ContractUTXOKey(outputID), data)
624         } else {
625                 store.batch.Set(ContractUTXOKey(outputID), data)
626         }
627         return nil
628 }
629
630 // SetGlobalTransactionIndex set global tx index by blockhash and position
631 func (store *MockWalletStore) SetGlobalTransactionIndex(globalTxID string, blockHash *bc.Hash, position uint64) {
632         if store.batch == nil {
633                 store.walletDB.Set(CalcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
634         } else {
635                 store.batch.Set(CalcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
636         }
637 }
638
639 // SetRecoveryStatus set recovery status
640 func (store *MockWalletStore) SetRecoveryStatus(recoveryState *RecoveryState) error {
641         rawStatus, err := json.Marshal(recoveryState)
642         if err != nil {
643                 return err
644         }
645         if store.batch == nil {
646                 store.walletDB.Set(dbm.RecoveryKey, rawStatus)
647         } else {
648                 store.batch.Set(dbm.RecoveryKey, rawStatus)
649         }
650         return nil
651 }
652
653 // SetTransaction set raw transaction by block height and tx position
654 func (store *MockWalletStore) SetTransaction(height uint64, tx *query.AnnotatedTx) error {
655         batch := store.walletDB.NewBatch()
656         if store.batch != nil {
657                 batch = store.batch
658         }
659
660         rawTx, err := json.Marshal(tx)
661         if err != nil {
662                 return err
663         }
664         batch.Set(calcAnnotatedKey(formatKey(height, tx.Position)), rawTx)
665         batch.Set(calcTxIndexKey(tx.ID.String()), []byte(formatKey(height, tx.Position)))
666
667         if store.batch == nil {
668                 batch.Write()
669         }
670         return nil
671 }
672
673 // SetUnconfirmedTransaction set unconfirmed tx by txID
674 func (store *MockWalletStore) SetUnconfirmedTransaction(txID string, tx *query.AnnotatedTx) error {
675         rawTx, err := json.Marshal(tx)
676         if err != nil {
677                 return err
678         }
679         if store.batch == nil {
680                 store.walletDB.Set(calcUnconfirmedTxKey(txID), rawTx)
681         } else {
682                 store.batch.Set(calcUnconfirmedTxKey(txID), rawTx)
683         }
684         return nil
685 }
686
687 // SetWalletInfo get wallet information
688 func (store *MockWalletStore) SetWalletInfo(status *StatusInfo) error {
689         rawWallet, err := json.Marshal(status)
690         if err != nil {
691                 return err
692         }
693
694         if store.batch == nil {
695                 store.walletDB.Set([]byte(dbm.WalletKey), rawWallet)
696         } else {
697                 store.batch.Set([]byte(dbm.WalletKey), rawWallet)
698         }
699         return nil
700 }
701
702 //-------------
703
704 type MockAccountStore struct {
705         accountDB dbm.DB
706         batch     dbm.Batch
707 }
708
709 // NewAccountStore create new MockAccountStore.
710 func NewMockAccountStore(db dbm.DB) *MockAccountStore {
711         return &MockAccountStore{
712                 accountDB: db,
713                 batch:     nil,
714         }
715 }
716
717 // InitBatch initial batch
718 func (store *MockAccountStore) InitBatch() error {
719         if store.batch != nil {
720                 return errors.New("MockAccountStore initail fail, store batch is not nil.")
721         }
722         store.batch = store.accountDB.NewBatch()
723         return nil
724 }
725
726 // CommitBatch commit batch
727 func (store *MockAccountStore) CommitBatch() error {
728         if store.batch == nil {
729                 return errors.New("MockAccountStore commit fail, store batch is nil.")
730         }
731         store.batch.Write()
732         store.batch = nil
733         return nil
734 }
735
736 // DeleteAccount set account account ID, account alias and raw account.
737 func (store *MockAccountStore) DeleteAccount(account *acc.Account) error {
738         batch := store.accountDB.NewBatch()
739         if store.batch != nil {
740                 batch = store.batch
741         }
742
743         // delete account utxos
744         store.deleteAccountUTXOs(account.ID)
745
746         // delete account control program
747         cps, err := store.ListControlPrograms()
748         if err != nil {
749                 return err
750         }
751         var hash [32]byte
752         for _, cp := range cps {
753                 if cp.AccountID == account.ID {
754                         sha3pool.Sum256(hash[:], cp.ControlProgram)
755                         batch.Delete(ContractKey(bc.NewHash(hash)))
756                 }
757         }
758
759         // delete bip44 contract index
760         batch.Delete(Bip44ContractIndexKey(account.ID, false))
761         batch.Delete(Bip44ContractIndexKey(account.ID, true))
762
763         // delete contract index
764         batch.Delete(contractIndexKey(account.ID))
765
766         // delete account id
767         batch.Delete(AccountIDKey(account.ID))
768         batch.Delete(accountAliasKey(account.Alias))
769         if store.batch == nil {
770                 batch.Write()
771         }
772         return nil
773 }
774
775 // deleteAccountUTXOs delete account utxos by accountID
776 func (store *MockAccountStore) deleteAccountUTXOs(accountID string) error {
777         batch := store.accountDB.NewBatch()
778         if store.batch != nil {
779                 batch = store.batch
780         }
781
782         accountUtxoIter := store.accountDB.IteratorPrefix([]byte(dbm.UTXOPrefix))
783         defer accountUtxoIter.Release()
784
785         for accountUtxoIter.Next() {
786                 accountUtxo := &acc.UTXO{}
787                 if err := json.Unmarshal(accountUtxoIter.Value(), accountUtxo); err != nil {
788                         return err
789                 }
790                 if accountID == accountUtxo.AccountID {
791                         batch.Delete(StandardUTXOKey(accountUtxo.OutputID))
792                 }
793         }
794
795         if store.batch == nil {
796                 batch.Write()
797         }
798         return nil
799 }
800
801 // DeleteStandardUTXO delete utxo by outpu id
802 func (store *MockAccountStore) DeleteStandardUTXO(outputID bc.Hash) {
803         if store.batch == nil {
804                 store.accountDB.Delete(StandardUTXOKey(outputID))
805         } else {
806                 store.batch.Delete(StandardUTXOKey(outputID))
807         }
808 }
809
810 // GetAccountByAlias get account by account alias
811 func (store *MockAccountStore) GetAccountByAlias(accountAlias string) (*acc.Account, error) {
812         accountID := store.accountDB.Get(accountAliasKey(accountAlias))
813         if accountID == nil {
814                 return nil, acc.ErrFindAccount
815         }
816         return store.GetAccountByID(string(accountID))
817 }
818
819 // GetAccountByID get account by accountID
820 func (store *MockAccountStore) GetAccountByID(accountID string) (*acc.Account, error) {
821         rawAccount := store.accountDB.Get(AccountIDKey(accountID))
822         if rawAccount == nil {
823                 return nil, acc.ErrFindAccount
824         }
825         account := new(acc.Account)
826         if err := json.Unmarshal(rawAccount, account); err != nil {
827                 return nil, err
828         }
829         return account, nil
830 }
831
832 // GetAccountIndex get account index by account xpubs
833 func (store *MockAccountStore) GetAccountIndex(xpubs []chainkd.XPub) uint64 {
834         currentIndex := uint64(0)
835         if rawIndexBytes := store.accountDB.Get(accountIndexKey(xpubs)); rawIndexBytes != nil {
836                 currentIndex = common.BytesToUnit64(rawIndexBytes)
837         }
838         return currentIndex
839 }
840
841 // GetBip44ContractIndex get bip44 contract index
842 func (store *MockAccountStore) GetBip44ContractIndex(accountID string, change bool) uint64 {
843         index := uint64(0)
844         if rawIndexBytes := store.accountDB.Get(Bip44ContractIndexKey(accountID, change)); rawIndexBytes != nil {
845                 index = common.BytesToUnit64(rawIndexBytes)
846         }
847         return index
848 }
849
850 // GetCoinbaseArbitrary get coinbase arbitrary
851 func (store *MockAccountStore) GetCoinbaseArbitrary() []byte {
852         return store.accountDB.Get([]byte(dbm.CoinbaseAbKey))
853 }
854
855 // GetContractIndex get contract index
856 func (store *MockAccountStore) GetContractIndex(accountID string) uint64 {
857         index := uint64(0)
858         if rawIndexBytes := store.accountDB.Get(contractIndexKey(accountID)); rawIndexBytes != nil {
859                 index = common.BytesToUnit64(rawIndexBytes)
860         }
861         return index
862 }
863
864 // GetControlProgram get control program
865 func (store *MockAccountStore) GetControlProgram(hash bc.Hash) (*acc.CtrlProgram, error) {
866         rawProgram := store.accountDB.Get(ContractKey(hash))
867         if rawProgram == nil {
868                 return nil, acc.ErrFindCtrlProgram
869         }
870         cp := new(acc.CtrlProgram)
871         if err := json.Unmarshal(rawProgram, cp); err != nil {
872                 return nil, err
873         }
874         return cp, nil
875 }
876
877 // GetMiningAddress get mining address
878 func (store *MockAccountStore) GetMiningAddress() (*acc.CtrlProgram, error) {
879         rawCP := store.accountDB.Get([]byte(dbm.MiningAddressKey))
880         if rawCP == nil {
881                 return nil, acc.ErrFindMiningAddress
882         }
883         cp := new(acc.CtrlProgram)
884         if err := json.Unmarshal(rawCP, cp); err != nil {
885                 return nil, err
886         }
887         return cp, nil
888 }
889
890 // GetUTXO get standard utxo by id
891 func (store *MockAccountStore) GetUTXO(outid bc.Hash) (*acc.UTXO, error) {
892         u := new(acc.UTXO)
893         if data := store.accountDB.Get(StandardUTXOKey(outid)); data != nil {
894                 return u, json.Unmarshal(data, u)
895         }
896         if data := store.accountDB.Get(ContractUTXOKey(outid)); data != nil {
897                 return u, json.Unmarshal(data, u)
898         }
899         return nil, acc.ErrMatchUTXO
900 }
901
902 // ListAccounts get all accounts which name prfix is id.
903 func (store *MockAccountStore) ListAccounts(id string) ([]*acc.Account, error) {
904         accounts := []*acc.Account{}
905         accountIter := store.accountDB.IteratorPrefix(AccountIDKey(strings.TrimSpace(id)))
906         defer accountIter.Release()
907
908         for accountIter.Next() {
909                 account := new(acc.Account)
910                 if err := json.Unmarshal(accountIter.Value(), &account); err != nil {
911                         return nil, err
912                 }
913                 accounts = append(accounts, account)
914         }
915         return accounts, nil
916 }
917
918 // ListControlPrograms get all local control programs
919 func (store *MockAccountStore) ListControlPrograms() ([]*acc.CtrlProgram, error) {
920         cps := []*acc.CtrlProgram{}
921         cpIter := store.accountDB.IteratorPrefix([]byte(ContractPrefix))
922         defer cpIter.Release()
923
924         for cpIter.Next() {
925                 cp := new(acc.CtrlProgram)
926                 if err := json.Unmarshal(cpIter.Value(), cp); err != nil {
927                         return nil, err
928                 }
929                 cps = append(cps, cp)
930         }
931         return cps, nil
932 }
933
934 // ListUTXOs get utxos by accountID
935 func (store *MockAccountStore) ListUTXOs() ([]*acc.UTXO, error) {
936         utxoIter := store.accountDB.IteratorPrefix([]byte(dbm.UTXOPrefix))
937         defer utxoIter.Release()
938
939         utxos := []*acc.UTXO{}
940         for utxoIter.Next() {
941                 utxo := new(acc.UTXO)
942                 if err := json.Unmarshal(utxoIter.Value(), utxo); err != nil {
943                         return nil, err
944                 }
945                 utxos = append(utxos, utxo)
946         }
947         return utxos, nil
948 }
949
950 // SetAccount set account account ID, account alias and raw account.
951 func (store *MockAccountStore) SetAccount(account *acc.Account) error {
952         rawAccount, err := json.Marshal(account)
953         if err != nil {
954                 return acc.ErrMarshalAccount
955         }
956
957         batch := store.accountDB.NewBatch()
958         if store.batch != nil {
959                 batch = store.batch
960         }
961
962         batch.Set(AccountIDKey(account.ID), rawAccount)
963         batch.Set(accountAliasKey(account.Alias), []byte(account.ID))
964
965         if store.batch == nil {
966                 batch.Write()
967         }
968         return nil
969 }
970
971 // SetAccountIndex update account index
972 func (store *MockAccountStore) SetAccountIndex(account *acc.Account) {
973         currentIndex := store.GetAccountIndex(account.XPubs)
974         if account.KeyIndex > currentIndex {
975                 if store.batch == nil {
976                         store.accountDB.Set(accountIndexKey(account.XPubs), common.Unit64ToBytes(account.KeyIndex))
977                 } else {
978                         store.batch.Set(accountIndexKey(account.XPubs), common.Unit64ToBytes(account.KeyIndex))
979                 }
980         }
981 }
982
983 // SetBip44ContractIndex set contract index
984 func (store *MockAccountStore) SetBip44ContractIndex(accountID string, change bool, index uint64) {
985         if store.batch == nil {
986                 store.accountDB.Set(Bip44ContractIndexKey(accountID, change), common.Unit64ToBytes(index))
987         } else {
988                 store.batch.Set(Bip44ContractIndexKey(accountID, change), common.Unit64ToBytes(index))
989         }
990 }
991
992 // SetCoinbaseArbitrary set coinbase arbitrary
993 func (store *MockAccountStore) SetCoinbaseArbitrary(arbitrary []byte) {
994         if store.batch == nil {
995                 store.accountDB.Set([]byte(dbm.CoinbaseAbKey), arbitrary)
996         } else {
997                 store.batch.Set([]byte(dbm.CoinbaseAbKey), arbitrary)
998         }
999 }
1000
1001 // SetContractIndex set contract index
1002 func (store *MockAccountStore) SetContractIndex(accountID string, index uint64) {
1003         if store.batch == nil {
1004                 store.accountDB.Set(contractIndexKey(accountID), common.Unit64ToBytes(index))
1005         } else {
1006                 store.batch.Set(contractIndexKey(accountID), common.Unit64ToBytes(index))
1007         }
1008 }
1009
1010 // SetControlProgram set raw program
1011 func (store *MockAccountStore) SetControlProgram(hash bc.Hash, program *acc.CtrlProgram) error {
1012         accountCP, err := json.Marshal(program)
1013         if err != nil {
1014                 return err
1015         }
1016         if store.batch == nil {
1017                 store.accountDB.Set(ContractKey(hash), accountCP)
1018         } else {
1019                 store.batch.Set(ContractKey(hash), accountCP)
1020         }
1021         return nil
1022 }
1023
1024 // SetMiningAddress set mining address
1025 func (store *MockAccountStore) SetMiningAddress(program *acc.CtrlProgram) error {
1026         rawProgram, err := json.Marshal(program)
1027         if err != nil {
1028                 return err
1029         }
1030
1031         if store.batch == nil {
1032                 store.accountDB.Set([]byte(dbm.MiningAddressKey), rawProgram)
1033         } else {
1034                 store.batch.Set([]byte(dbm.MiningAddressKey), rawProgram)
1035         }
1036         return nil
1037 }
1038
1039 // SetStandardUTXO set standard utxo
1040 func (store *MockAccountStore) SetStandardUTXO(outputID bc.Hash, utxo *acc.UTXO) error {
1041         data, err := json.Marshal(utxo)
1042         if err != nil {
1043                 return err
1044         }
1045         if store.batch == nil {
1046                 store.accountDB.Set(StandardUTXOKey(outputID), data)
1047         } else {
1048                 store.batch.Set(StandardUTXOKey(outputID), data)
1049         }
1050         return nil
1051 }