OSDN Git Service

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