OSDN Git Service

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