OSDN Git Service

rename calcGlobalTxIndexKey to CalcGlobalTxIndexKey
[bytom/vapor.git] / database / wallet_store.go
1 package database
2
3 import (
4         "encoding/binary"
5         "encoding/json"
6         "fmt"
7         "sort"
8
9         "github.com/vapor/wallet"
10
11         acc "github.com/vapor/account"
12         "github.com/vapor/asset"
13         "github.com/vapor/blockchain/query"
14         "github.com/vapor/blockchain/signers"
15         "github.com/vapor/crypto/ed25519/chainkd"
16         "github.com/vapor/crypto/sha3pool"
17         dbm "github.com/vapor/database/leveldb"
18         "github.com/vapor/errors"
19         "github.com/vapor/protocol/bc"
20 )
21
22 // errors
23 var (
24         // ErrFindAccount        = errors.New("Failed to find account")
25         errAccntTxIDNotFound = errors.New("account TXID not found")
26         errGetAsset          = errors.New("Failed to find asset definition")
27 )
28
29 func accountIndexKey(xpubs []chainkd.XPub) []byte {
30         var hash [32]byte
31         var xPubs []byte
32         cpy := append([]chainkd.XPub{}, xpubs[:]...)
33         sort.Sort(signers.SortKeys(cpy))
34         for _, xpub := range cpy {
35                 xPubs = append(xPubs, xpub[:]...)
36         }
37         sha3pool.Sum256(hash[:], xPubs)
38         return append([]byte(dbm.AccountIndexPrefix), hash[:]...)
39 }
40
41 func Bip44ContractIndexKey(accountID string, change bool) []byte {
42         key := append([]byte(dbm.ContractIndexPrefix), accountID...)
43         if change {
44                 return append(key, []byte{1}...)
45         }
46         return append(key, []byte{0}...)
47 }
48
49 // ContractKey account control promgram store prefix
50 func ContractKey(hash bc.Hash) []byte {
51         return append([]byte(dbm.ContractPrefix), hash.Bytes()...)
52 }
53
54 // AccountIDKey account id store prefix
55 func AccountIDKey(accountID string) []byte {
56         return append([]byte(dbm.AccountPrefix), []byte(accountID)...)
57 }
58
59 // StandardUTXOKey makes an account unspent outputs key to store
60 func StandardUTXOKey(id bc.Hash) []byte {
61         return append(dbm.UTXOPrefix, id.Bytes()...)
62 }
63
64 // ContractUTXOKey makes a smart contract unspent outputs key to store
65 func ContractUTXOKey(id bc.Hash) []byte {
66         return append(dbm.SUTXOPrefix, id.Bytes()...)
67 }
68
69 func calcDeleteKey(blockHeight uint64) []byte {
70         return []byte(fmt.Sprintf("%s%016x", dbm.TxPrefix, blockHeight))
71 }
72
73 func calcTxIndexKey(txID string) []byte {
74         return append(dbm.TxIndexPrefix, []byte(txID)...)
75 }
76
77 func calcAnnotatedKey(formatKey string) []byte {
78         return append(dbm.TxPrefix, []byte(formatKey)...)
79 }
80
81 func calcUnconfirmedTxKey(formatKey string) []byte {
82         return append(dbm.UnconfirmedTxPrefix, []byte(formatKey)...)
83 }
84
85 func CalcGlobalTxIndexKey(txID string) []byte {
86         return append(dbm.GlobalTxIndexPrefix, []byte(txID)...)
87 }
88
89 func CalcGlobalTxIndex(blockHash *bc.Hash, position uint64) []byte {
90         txIdx := make([]byte, 40)
91         copy(txIdx[:32], blockHash.Bytes())
92         binary.BigEndian.PutUint64(txIdx[32:], position)
93         return txIdx
94 }
95
96 func formatKey(blockHeight uint64, position uint32) string {
97         return fmt.Sprintf("%016x%08x", blockHeight, position)
98 }
99
100 func contractIndexKey(accountID string) []byte {
101         return append([]byte(dbm.ContractIndexPrefix), []byte(accountID)...)
102 }
103
104 func accountAliasKey(name string) []byte {
105         return append([]byte(dbm.AccountAliasPrefix), []byte(name)...)
106 }
107
108 // WalletStore store wallet using leveldb
109 type WalletStore struct {
110         walletDB dbm.DB
111         batch    dbm.Batch
112 }
113
114 // NewWalletStore create new WalletStore struct
115 func NewWalletStore(db dbm.DB) *WalletStore {
116         return &WalletStore{
117                 walletDB: db,
118                 batch:    nil,
119         }
120 }
121
122 // InitBatch initial batch
123 func (store *WalletStore) InitBatch() error {
124         if store.batch != nil {
125                 return errors.New("WalletStore initail fail, store batch is not nil.")
126         }
127         store.batch = store.walletDB.NewBatch()
128         return nil
129 }
130
131 // CommitBatch commit batch
132 func (store *WalletStore) CommitBatch() error {
133         if store.batch == nil {
134                 return errors.New("WalletStore commit fail, store batch is nil.")
135         }
136         store.batch.Write()
137         store.batch = nil
138         return nil
139 }
140
141 // DeleteContractUTXO delete contract utxo by outputID
142 func (store *WalletStore) DeleteContractUTXO(outputID bc.Hash) {
143         if store.batch == nil {
144                 store.walletDB.Delete(ContractUTXOKey(outputID))
145         } else {
146                 store.batch.Delete(ContractUTXOKey(outputID))
147         }
148 }
149
150 // DeleteRecoveryStatus delete recovery status
151 func (store *WalletStore) DeleteRecoveryStatus() {
152         if store.batch == nil {
153                 store.walletDB.Delete(dbm.RecoveryKey)
154         } else {
155                 store.batch.Delete(dbm.RecoveryKey)
156         }
157 }
158
159 // DeleteTransactions delete transactions when orphan block rollback
160 func (store *WalletStore) DeleteTransactions(height uint64) {
161         batch := store.walletDB.NewBatch()
162         if store.batch != nil {
163                 batch = store.batch
164         }
165         txIter := store.walletDB.IteratorPrefix(calcDeleteKey(height))
166         defer txIter.Release()
167
168         tmpTx := query.AnnotatedTx{}
169         for txIter.Next() {
170                 if err := json.Unmarshal(txIter.Value(), &tmpTx); err == nil {
171                         batch.Delete(calcTxIndexKey(tmpTx.ID.String()))
172                 }
173                 batch.Delete(txIter.Key())
174         }
175         if store.batch == nil {
176                 batch.Write()
177         }
178 }
179
180 // DeleteUnconfirmedTransaction delete unconfirmed tx by txID
181 func (store *WalletStore) DeleteUnconfirmedTransaction(txID string) {
182         if store.batch == nil {
183                 store.walletDB.Delete(calcUnconfirmedTxKey(txID))
184         } else {
185                 store.batch.Delete(calcUnconfirmedTxKey(txID))
186         }
187 }
188
189 // DeleteWalletTransactions delete all txs in wallet
190 func (store *WalletStore) DeleteWalletTransactions() {
191         batch := store.walletDB.NewBatch()
192         if store.batch != nil {
193                 batch = store.batch
194         }
195         txIter := store.walletDB.IteratorPrefix([]byte(dbm.TxPrefix))
196         defer txIter.Release()
197
198         for txIter.Next() {
199                 batch.Delete(txIter.Key())
200         }
201
202         txIndexIter := store.walletDB.IteratorPrefix([]byte(dbm.TxIndexPrefix))
203         defer txIndexIter.Release()
204
205         for txIndexIter.Next() {
206                 batch.Delete(txIndexIter.Key())
207         }
208         if store.batch == nil {
209                 batch.Write()
210         }
211 }
212
213 // DeleteWalletUTXOs delete all txs in wallet
214 func (store *WalletStore) DeleteWalletUTXOs() {
215         batch := store.walletDB.NewBatch()
216         if store.batch != nil {
217                 batch = store.batch
218         }
219         ruIter := store.walletDB.IteratorPrefix([]byte(dbm.UTXOPrefix))
220         defer ruIter.Release()
221         for ruIter.Next() {
222                 batch.Delete(ruIter.Key())
223         }
224
225         suIter := store.walletDB.IteratorPrefix([]byte(dbm.SUTXOPrefix))
226         defer suIter.Release()
227         for suIter.Next() {
228                 batch.Delete(suIter.Key())
229         }
230         if store.batch == nil {
231                 batch.Write()
232         }
233 }
234
235 // GetAsset get asset by assetID
236 func (store *WalletStore) GetAsset(assetID *bc.AssetID) (*asset.Asset, error) {
237         definitionByte := store.walletDB.Get(asset.ExtAssetKey(assetID))
238         if definitionByte == nil {
239                 return nil, errGetAsset
240         }
241         definitionMap := make(map[string]interface{})
242         if err := json.Unmarshal(definitionByte, &definitionMap); err != nil {
243                 return nil, err
244         }
245         alias := assetID.String()
246         externalAsset := &asset.Asset{
247                 AssetID:           *assetID,
248                 Alias:             &alias,
249                 DefinitionMap:     definitionMap,
250                 RawDefinitionByte: definitionByte,
251         }
252         return externalAsset, nil
253 }
254
255 // GetControlProgram get raw program by hash
256 func (store *WalletStore) GetControlProgram(hash bc.Hash) (*acc.CtrlProgram, error) {
257         rawProgram := store.walletDB.Get(ContractKey(hash))
258         if rawProgram == nil {
259                 return nil, acc.ErrFindCtrlProgram
260         }
261         accountCP := new(acc.CtrlProgram)
262         if err := json.Unmarshal(rawProgram, &accountCP); err != nil {
263                 return nil, err
264         }
265         return accountCP, nil
266 }
267
268 // GetGlobalTransactionIndex get global tx by txID
269 func (store *WalletStore) GetGlobalTransactionIndex(txID string) []byte {
270         return store.walletDB.Get(CalcGlobalTxIndexKey(txID))
271 }
272
273 // GetStandardUTXO get standard utxo by id
274 func (store *WalletStore) GetStandardUTXO(outid bc.Hash) (*acc.UTXO, error) {
275         rawUTXO := store.walletDB.Get(StandardUTXOKey(outid))
276         if rawUTXO == nil {
277                 return nil, fmt.Errorf("failed get standard UTXO, outputID: %s ", outid.String())
278         }
279         UTXO := new(acc.UTXO)
280         if err := json.Unmarshal(rawUTXO, UTXO); err != nil {
281                 return nil, err
282         }
283         return UTXO, nil
284 }
285
286 // GetTransaction get tx by txid
287 func (store *WalletStore) GetTransaction(txID string) (*query.AnnotatedTx, error) {
288         formatKey := store.walletDB.Get(calcTxIndexKey(txID))
289         if formatKey == nil {
290                 return nil, errAccntTxIDNotFound
291         }
292         rawTx := store.walletDB.Get(calcAnnotatedKey(string(formatKey)))
293         tx := new(query.AnnotatedTx)
294         if err := json.Unmarshal(rawTx, tx); err != nil {
295                 return nil, err
296         }
297         return tx, nil
298 }
299
300 // GetUnconfirmedTransaction get unconfirmed tx by txID
301 func (store *WalletStore) GetUnconfirmedTransaction(txID string) (*query.AnnotatedTx, error) {
302         rawUnconfirmedTx := store.walletDB.Get(calcUnconfirmedTxKey(txID))
303         if rawUnconfirmedTx == nil {
304                 return nil, fmt.Errorf("failed get unconfirmed tx, txID: %s ", txID)
305         }
306         tx := new(query.AnnotatedTx)
307         if err := json.Unmarshal(rawUnconfirmedTx, tx); err != nil {
308                 return nil, err
309         }
310         return tx, nil
311 }
312
313 // GetRecoveryStatus delete recovery status
314 func (store *WalletStore) GetRecoveryStatus() (*wallet.RecoveryState, error) {
315         rawStatus := store.walletDB.Get(dbm.RecoveryKey)
316         if rawStatus == nil {
317                 return nil, wallet.ErrGetRecoveryStatus
318         }
319         state := new(wallet.RecoveryState)
320         if err := json.Unmarshal(rawStatus, state); err != nil {
321                 return nil, err
322         }
323         return state, nil
324 }
325
326 // GetWalletInfo get wallet information
327 func (store *WalletStore) GetWalletInfo() (*wallet.StatusInfo, error) {
328         rawStatus := store.walletDB.Get([]byte(dbm.WalletKey))
329         if rawStatus == nil {
330                 return nil, wallet.ErrGetWalletStatusInfo
331         }
332         status := new(wallet.StatusInfo)
333         if err := json.Unmarshal(rawStatus, status); err != nil {
334                 return nil, err
335         }
336         return status, nil
337 }
338
339 // ListAccountUTXOs get all account unspent outputs
340 func (store *WalletStore) ListAccountUTXOs(key string) ([]*acc.UTXO, error) {
341         accountUtxoIter := store.walletDB.IteratorPrefix([]byte(key))
342         defer accountUtxoIter.Release()
343
344         confirmedUTXOs := []*acc.UTXO{}
345         for accountUtxoIter.Next() {
346                 utxo := new(acc.UTXO)
347                 if err := json.Unmarshal(accountUtxoIter.Value(), utxo); err != nil {
348                         return nil, err
349                 }
350                 confirmedUTXOs = append(confirmedUTXOs, utxo)
351         }
352         return confirmedUTXOs, nil
353 }
354
355 func (store *WalletStore) ListTransactions(accountID string, StartTxID string, count uint, unconfirmed bool) ([]*query.AnnotatedTx, error) {
356         annotatedTxs := []*query.AnnotatedTx{}
357         var startKey []byte
358         preFix := dbm.TxPrefix
359
360         if StartTxID != "" {
361                 if unconfirmed {
362                         startKey = calcUnconfirmedTxKey(StartTxID)
363                 } else {
364                         formatKey := store.walletDB.Get(calcTxIndexKey(StartTxID))
365                         if formatKey == nil {
366                                 return nil, errAccntTxIDNotFound
367                         }
368                         startKey = calcAnnotatedKey(string(formatKey))
369                 }
370         }
371
372         if unconfirmed {
373                 preFix = dbm.UnconfirmedTxPrefix
374         }
375
376         itr := store.walletDB.IteratorPrefixWithStart([]byte(preFix), startKey, true)
377         defer itr.Release()
378
379         for txNum := count; itr.Next() && txNum > 0; txNum-- {
380                 annotatedTx := new(query.AnnotatedTx)
381                 if err := json.Unmarshal(itr.Value(), &annotatedTx); err != nil {
382                         return nil, err
383                 }
384                 annotatedTxs = append(annotatedTxs, annotatedTx)
385         }
386
387         return annotatedTxs, nil
388 }
389
390 // ListUnconfirmedTransactions get all unconfirmed txs
391 func (store *WalletStore) ListUnconfirmedTransactions() ([]*query.AnnotatedTx, error) {
392         annotatedTxs := []*query.AnnotatedTx{}
393         txIter := store.walletDB.IteratorPrefix([]byte(dbm.UnconfirmedTxPrefix))
394         defer txIter.Release()
395
396         for txIter.Next() {
397                 annotatedTx := &query.AnnotatedTx{}
398                 if err := json.Unmarshal(txIter.Value(), &annotatedTx); err != nil {
399                         return nil, err
400                 }
401                 annotatedTxs = append(annotatedTxs, annotatedTx)
402         }
403         return annotatedTxs, nil
404 }
405
406 // SetAssetDefinition set assetID and definition
407 func (store *WalletStore) SetAssetDefinition(assetID *bc.AssetID, definition []byte) {
408         if store.batch == nil {
409                 store.walletDB.Set(asset.ExtAssetKey(assetID), definition)
410         } else {
411                 store.batch.Set(asset.ExtAssetKey(assetID), definition)
412         }
413 }
414
415 // SetContractUTXO set standard utxo
416 func (store *WalletStore) SetContractUTXO(outputID bc.Hash, utxo *acc.UTXO) error {
417         data, err := json.Marshal(utxo)
418         if err != nil {
419                 return err
420         }
421         if store.batch == nil {
422                 store.walletDB.Set(ContractUTXOKey(outputID), data)
423         } else {
424                 store.batch.Set(ContractUTXOKey(outputID), data)
425         }
426         return nil
427 }
428
429 // SetGlobalTransactionIndex set global tx index by blockhash and position
430 func (store *WalletStore) SetGlobalTransactionIndex(globalTxID string, blockHash *bc.Hash, position uint64) {
431         if store.batch == nil {
432                 store.walletDB.Set(CalcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
433         } else {
434                 store.batch.Set(CalcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
435         }
436 }
437
438 // SetRecoveryStatus set recovery status
439 func (store *WalletStore) SetRecoveryStatus(recoveryState *wallet.RecoveryState) error {
440         rawStatus, err := json.Marshal(recoveryState)
441         if err != nil {
442                 return err
443         }
444         if store.batch == nil {
445                 store.walletDB.Set(dbm.RecoveryKey, rawStatus)
446         } else {
447                 store.batch.Set(dbm.RecoveryKey, rawStatus)
448         }
449         return nil
450 }
451
452 // SetTransaction set raw transaction by block height and tx position
453 func (store *WalletStore) SetTransaction(height uint64, tx *query.AnnotatedTx) error {
454         batch := store.walletDB.NewBatch()
455         if store.batch != nil {
456                 batch = store.batch
457         }
458
459         rawTx, err := json.Marshal(tx)
460         if err != nil {
461                 return err
462         }
463         batch.Set(calcAnnotatedKey(formatKey(height, tx.Position)), rawTx)
464         batch.Set(calcTxIndexKey(tx.ID.String()), []byte(formatKey(height, tx.Position)))
465
466         if store.batch == nil {
467                 batch.Write()
468         }
469         return nil
470 }
471
472 // SetUnconfirmedTransaction set unconfirmed tx by txID
473 func (store *WalletStore) SetUnconfirmedTransaction(txID string, tx *query.AnnotatedTx) error {
474         rawTx, err := json.Marshal(tx)
475         if err != nil {
476                 return err
477         }
478         if store.batch == nil {
479                 store.walletDB.Set(calcUnconfirmedTxKey(txID), rawTx)
480         } else {
481                 store.batch.Set(calcUnconfirmedTxKey(txID), rawTx)
482         }
483         return nil
484 }
485
486 // SetWalletInfo get wallet information
487 func (store *WalletStore) SetWalletInfo(status *wallet.StatusInfo) error {
488         rawWallet, err := json.Marshal(status)
489         if err != nil {
490                 return err
491         }
492
493         if store.batch == nil {
494                 store.walletDB.Set([]byte(dbm.WalletKey), rawWallet)
495         } else {
496                 store.batch.Set([]byte(dbm.WalletKey), rawWallet)
497         }
498         return nil
499 }