OSDN Git Service

add walletbatch
[bytom/vapor.git] / database / wallet_store.go
1 package database
2
3 import (
4         "encoding/binary"
5         "encoding/json"
6         "fmt"
7         "reflect"
8
9         "github.com/vapor/asset"
10         "github.com/vapor/blockchain/query"
11         "github.com/vapor/common"
12         dbm "github.com/vapor/database/leveldb"
13         "github.com/vapor/errors"
14         "github.com/vapor/protocol/bc"
15 )
16
17 var errAccntTxIDNotFound = errors.New("account TXID not found")
18
19 const (
20         UTXOPrefix          = "ACU:" //UTXOPrefix is StandardUTXOKey prefix
21         SUTXOPrefix         = "SCU:" //SUTXOPrefix is ContractUTXOKey prefix
22         contractPrefix      = "Contract:"
23         accountPrefix       = "Account:"
24         TxPrefix            = "TXS:"  //TxPrefix is wallet database transactions prefix
25         TxIndexPrefix       = "TID:"  //TxIndexPrefix is wallet database tx index prefix
26         UnconfirmedTxPrefix = "UTXS:" //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
27         GlobalTxIndexPrefix = "GTID:" //GlobalTxIndexPrefix is wallet database global tx index prefix
28         walletKey           = "walletInfo"
29 )
30
31 // WalletStorer interface contains wallet storage functions.
32 type WalletStorer interface {
33         InitBatch()
34         CommitBatch()
35         GetAssetDefinition(*bc.AssetID) []byte
36         SetAssetDefinition(*bc.AssetID, []byte)
37         GetRawProgram(common.Hash) []byte
38         GetAccountByAccountID(string) []byte
39         DeleteTransactions(uint64)
40         SetTransaction(uint64, uint32, string, []byte)
41         DeleteUnconfirmedTransaction(string)
42         SetGlobalTransactionIndex(string, *bc.Hash, uint64)
43         GetStandardUTXO(bc.Hash) []byte
44         GetTransaction(string) ([]byte, error)
45         GetGlobalTransaction(string) []byte
46         GetTransactions() ([]*query.AnnotatedTx, error)
47         GetUnconfirmedTransactions() ([]*query.AnnotatedTx, error)
48         GetUnconfirmedTransaction(string) []byte
49         SetUnconfirmedTransaction(string, []byte)
50         DeleteStardardUTXO(bc.Hash)
51         DeleteContractUTXO(bc.Hash)
52         SetStandardUTXO(bc.Hash, []byte)
53         SetContractUTXO(bc.Hash, []byte)
54         GetWalletInfo() []byte
55         SetWalletInfo([]byte)
56         DeleteWalletTransactions()
57         DeleteWalletUTXOs()
58         GetAccountUTXOs(key string) [][]byte
59         SetRecoveryStatus([]byte, []byte)
60         DeleteRecoveryStatus([]byte)
61         GetRecoveryStatus([]byte) []byte
62 }
63
64 // WalletStore store wallet using leveldb
65 type WalletStore struct {
66         DB    dbm.DB
67         batch dbm.Batch
68 }
69
70 type WalletBatch struct {
71         batch dbm.Batch
72 }
73
74 // NewWalletStore create new WalletStore struct
75 func NewWalletStore(db dbm.DB) *WalletStore {
76         return &WalletStore{
77                 DB:    db,
78                 batch: nil,
79         }
80 }
81
82 // InitBatch initial batch
83 func (store *WalletStore) InitBatch() {
84         if store.batch == nil {
85                 store.batch = store.DB.NewBatch()
86                 fmt.Println("InitBatch type of store.batch is:", reflect.TypeOf(store.batch))
87                 fmt.Println("InitBatch value of store.batch is: ", reflect.ValueOf(store.batch))
88                 fmt.Printf("InitBatch store.batch pointer is: %p\n", store.batch)
89         }
90 }
91
92 // CommitBatch commit batch
93 func (store *WalletStore) CommitBatch() {
94         fmt.Println("CommitBatch...")
95         if store.batch != nil {
96                 fmt.Println("CommitBatch not nil...")
97                 fmt.Println("CommitBatch type of store.batch is:", reflect.TypeOf(store.batch))
98                 fmt.Println("CommitBatch value of store.batch is: ", reflect.ValueOf(store.batch))
99                 fmt.Printf("CommitBatch store.batch pointer is: %p\n", store.batch)
100
101                 store.batch.Write()
102                 // store.batch = store.DB.NewBatch()
103         }
104 }
105
106 // ContractKey account control promgram store prefix
107 func ContractKey(hash common.Hash) []byte {
108         return append([]byte(contractPrefix), hash[:]...)
109 }
110
111 // Key account store prefix
112 func Key(name string) []byte {
113         return append([]byte(accountPrefix), []byte(name)...)
114 }
115
116 // StandardUTXOKey makes an account unspent outputs key to store
117 func StandardUTXOKey(id bc.Hash) []byte {
118         name := id.String()
119         return []byte(UTXOPrefix + name)
120 }
121
122 // ContractUTXOKey makes a smart contract unspent outputs key to store
123 func ContractUTXOKey(id bc.Hash) []byte {
124         name := id.String()
125         return []byte(SUTXOPrefix + name)
126 }
127
128 func calcDeleteKey(blockHeight uint64) []byte {
129         return []byte(fmt.Sprintf("%s%016x", TxPrefix, blockHeight))
130 }
131
132 func calcTxIndexKey(txID string) []byte {
133         return []byte(TxIndexPrefix + txID)
134 }
135
136 func calcAnnotatedKey(formatKey string) []byte {
137         return []byte(TxPrefix + formatKey)
138 }
139
140 func calcUnconfirmedTxKey(formatKey string) []byte {
141         return []byte(UnconfirmedTxPrefix + formatKey)
142 }
143
144 func calcGlobalTxIndexKey(txID string) []byte {
145         return []byte(GlobalTxIndexPrefix + txID)
146 }
147
148 func CalcGlobalTxIndex(blockHash *bc.Hash, position uint64) []byte {
149         txIdx := make([]byte, 40)
150         copy(txIdx[:32], blockHash.Bytes())
151         binary.BigEndian.PutUint64(txIdx[32:], position)
152         return txIdx
153 }
154
155 func formatKey(blockHeight uint64, position uint32) string {
156         return fmt.Sprintf("%016x%08x", blockHeight, position)
157 }
158
159 // GetAssetDefinition get asset definition by assetiD
160 func (store *WalletStore) GetAssetDefinition(assetID *bc.AssetID) []byte {
161         return store.DB.Get(asset.ExtAssetKey(assetID))
162 }
163
164 // SetAssetDefinition set assetID and definition
165 func (store *WalletStore) SetAssetDefinition(assetID *bc.AssetID, definition []byte) {
166         if store.batch == nil {
167                 store.DB.Set(asset.ExtAssetKey(assetID), definition)
168         } else {
169                 store.batch.Set(asset.ExtAssetKey(assetID), definition)
170         }
171 }
172
173 // GetRawProgram get raw program by hash
174 func (store *WalletStore) GetRawProgram(hash common.Hash) []byte {
175         return store.DB.Get(ContractKey(hash))
176 }
177
178 // GetAccountByAccountID get account value by account ID
179 func (store *WalletStore) GetAccountByAccountID(accountID string) []byte {
180         return store.DB.Get(Key(accountID))
181 }
182
183 // DeleteTransactions delete transactions when orphan block rollback
184 func (store *WalletStore) DeleteTransactions(height uint64) {
185         tmpTx := query.AnnotatedTx{}
186         batch := store.DB.NewBatch()
187         if store.batch != nil {
188                 batch = store.batch
189         }
190         txIter := store.DB.IteratorPrefix(calcDeleteKey(height))
191         defer txIter.Release()
192
193         for txIter.Next() {
194                 if err := json.Unmarshal(txIter.Value(), &tmpTx); err == nil {
195                         batch.Delete(calcTxIndexKey(tmpTx.ID.String()))
196                 }
197                 batch.Delete(txIter.Key())
198         }
199         if store.batch == nil {
200                 batch.Write()
201         }
202 }
203
204 // SetTransaction set raw transaction by block height and tx position
205 func (store *WalletStore) SetTransaction(height uint64, position uint32, txID string, rawTx []byte) {
206         fmt.Println("SetTransaction...")
207         if store.batch == nil {
208                 fmt.Println("SetTransaction ... nil")
209                 batch := store.DB.NewBatch()
210                 fmt.Println("type of batch is:", reflect.TypeOf(batch))
211                 batch.Set(calcAnnotatedKey(formatKey(height, position)), rawTx)
212                 batch.Set(calcTxIndexKey(txID), []byte(formatKey(height, position)))
213                 batch.Write()
214         } else {
215                 fmt.Println("SetTransaction ... not nil")
216                 // store.InitBatch()
217                 // store.batch = store.DB.NewBatch()
218                 fmt.Println("type of store.batch is:", reflect.TypeOf(store.batch))
219                 store.batch.Set(calcAnnotatedKey(formatKey(height, position)), rawTx)
220                 store.batch.Set(calcTxIndexKey(txID), []byte(formatKey(height, position)))
221                 // store.CommitBatch()
222                 // store.batch.Write()
223         }
224 }
225
226 // DeleteUnconfirmedTransaction delete unconfirmed tx by txID
227 func (store *WalletStore) DeleteUnconfirmedTransaction(txID string) {
228         if store.batch == nil {
229                 store.DB.Delete(calcUnconfirmedTxKey(txID))
230         } else {
231                 store.batch.Delete(calcUnconfirmedTxKey(txID))
232         }
233 }
234
235 // SetGlobalTransactionIndex set global tx index by blockhash and position
236 func (store *WalletStore) SetGlobalTransactionIndex(globalTxID string, blockHash *bc.Hash, position uint64) {
237         if store.batch == nil {
238                 store.DB.Set(calcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
239         } else {
240                 store.batch.Set(calcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
241         }
242 }
243
244 // GetStandardUTXO get standard utxo by id
245 func (store *WalletStore) GetStandardUTXO(outid bc.Hash) []byte {
246         return store.DB.Get(StandardUTXOKey(outid))
247 }
248
249 // GetTransaction get tx by tx index
250 func (store *WalletStore) GetTransaction(txID string) ([]byte, error) {
251         fmt.Println("GetTransaction... txID: ", txID)
252         formatKey := store.DB.Get(calcTxIndexKey(txID))
253         if formatKey == nil {
254                 return nil, errAccntTxIDNotFound
255         }
256         txInfo := store.DB.Get(calcAnnotatedKey(string(formatKey)))
257         return txInfo, nil
258 }
259
260 // GetGlobalTransaction get global tx by txID
261 func (store *WalletStore) GetGlobalTransaction(txID string) []byte {
262         return store.DB.Get(calcGlobalTxIndexKey(txID))
263 }
264
265 // GetTransactions get all walletDB transactions
266 func (store *WalletStore) GetTransactions() ([]*query.AnnotatedTx, error) {
267         annotatedTxs := []*query.AnnotatedTx{}
268
269         txIter := store.DB.IteratorPrefix([]byte(TxPrefix))
270         defer txIter.Release()
271         for txIter.Next() {
272                 annotatedTx := &query.AnnotatedTx{}
273                 if err := json.Unmarshal(txIter.Value(), &annotatedTx); err != nil {
274                         return nil, err
275                 }
276                 annotatedTxs = append(annotatedTxs, annotatedTx)
277         }
278
279         return annotatedTxs, nil
280 }
281
282 // GetUnconfirmedTransactions get all unconfirmed txs
283 func (store *WalletStore) GetUnconfirmedTransactions() ([]*query.AnnotatedTx, error) {
284         annotatedTxs := []*query.AnnotatedTx{}
285         txIter := store.DB.IteratorPrefix([]byte(UnconfirmedTxPrefix))
286         defer txIter.Release()
287
288         for txIter.Next() {
289                 annotatedTx := &query.AnnotatedTx{}
290                 if err := json.Unmarshal(txIter.Value(), &annotatedTx); err != nil {
291                         return nil, err
292                 }
293                 annotatedTxs = append(annotatedTxs, annotatedTx)
294         }
295         return annotatedTxs, nil
296 }
297
298 // GetUnconfirmedTransaction get unconfirmed tx by txID
299 func (store *WalletStore) GetUnconfirmedTransaction(txID string) []byte {
300         return store.DB.Get(calcUnconfirmedTxKey(txID))
301 }
302
303 // SetUnconfirmedTransaction set unconfirmed tx by txID
304 func (store *WalletStore) SetUnconfirmedTransaction(txID string, rawTx []byte) {
305         if store.batch == nil {
306                 store.DB.Set(calcUnconfirmedTxKey(txID), rawTx)
307         } else {
308                 store.batch.Set(calcUnconfirmedTxKey(txID), rawTx)
309         }
310 }
311
312 // DeleteStardardUTXO delete stardard utxo by outputID
313 func (store *WalletStore) DeleteStardardUTXO(outputID bc.Hash) {
314         if store.batch == nil {
315                 store.DB.Delete(StandardUTXOKey(outputID))
316         } else {
317                 store.batch.Delete(StandardUTXOKey(outputID))
318         }
319 }
320
321 // DeleteContractUTXO delete contract utxo by outputID
322 func (store *WalletStore) DeleteContractUTXO(outputID bc.Hash) {
323         if store.batch == nil {
324                 store.DB.Delete(ContractUTXOKey(outputID))
325         } else {
326                 store.batch.Delete(ContractUTXOKey(outputID))
327         }
328 }
329
330 // SetStandardUTXO set standard utxo
331 func (store *WalletStore) SetStandardUTXO(outputID bc.Hash, data []byte) {
332         if store.batch == nil {
333                 store.DB.Set(StandardUTXOKey(outputID), data)
334         } else {
335                 store.batch.Set(StandardUTXOKey(outputID), data)
336         }
337 }
338
339 // SetContractUTXO set standard utxo
340 func (store *WalletStore) SetContractUTXO(outputID bc.Hash, data []byte) {
341         if store.batch == nil {
342                 store.DB.Set(ContractUTXOKey(outputID), data)
343         } else {
344                 store.batch.Set(ContractUTXOKey(outputID), data)
345         }
346 }
347
348 // GetWalletInfo get wallet information
349 func (store *WalletStore) GetWalletInfo() []byte {
350         return store.DB.Get([]byte(walletKey))
351 }
352
353 // SetWalletInfo get wallet information
354 func (store *WalletStore) SetWalletInfo(rawWallet []byte) {
355         if store.batch == nil {
356                 store.DB.Set([]byte(walletKey), rawWallet)
357         } else {
358                 store.batch.Set([]byte(walletKey), rawWallet)
359         }
360 }
361
362 // DeleteWalletTransactions delete all txs in wallet
363 func (store *WalletStore) DeleteWalletTransactions() {
364         batch := store.DB.NewBatch()
365         txIter := store.DB.IteratorPrefix([]byte(TxPrefix))
366         defer txIter.Release()
367
368         for txIter.Next() {
369                 batch.Delete(txIter.Key())
370         }
371
372         txIndexIter := store.DB.IteratorPrefix([]byte(TxIndexPrefix))
373         defer txIndexIter.Release()
374
375         for txIndexIter.Next() {
376                 batch.Delete(txIndexIter.Key())
377         }
378         batch.Write()
379 }
380
381 // DeleteWalletUTXOs delete all txs in wallet
382 func (store *WalletStore) DeleteWalletUTXOs() {
383         batch := store.DB.NewBatch()
384         ruIter := store.DB.IteratorPrefix([]byte(UTXOPrefix))
385         defer ruIter.Release()
386         for ruIter.Next() {
387                 batch.Delete(ruIter.Key())
388         }
389
390         suIter := store.DB.IteratorPrefix([]byte(SUTXOPrefix))
391         defer suIter.Release()
392         for suIter.Next() {
393                 batch.Delete(suIter.Key())
394         }
395         batch.Write()
396 }
397
398 // GetAccountUTXOs get all account unspent outputs
399 func (store *WalletStore) GetAccountUTXOs(key string) [][]byte {
400         accountUtxoIter := store.DB.IteratorPrefix([]byte(key))
401         defer accountUtxoIter.Release()
402
403         rawUTXOs := make([][]byte, 0)
404         for accountUtxoIter.Next() {
405                 utxo := accountUtxoIter.Value()
406                 rawUTXOs = append(rawUTXOs, utxo)
407         }
408         return rawUTXOs
409 }
410
411 // SetRecoveryStatus set recovery status
412 func (store *WalletStore) SetRecoveryStatus(recoveryKey, rawStatus []byte) {
413         if store.batch == nil {
414                 store.DB.Set(recoveryKey, rawStatus)
415         } else {
416                 store.batch.Set(recoveryKey, rawStatus)
417         }
418 }
419
420 // DeleteRecoveryStatus delete recovery status
421 func (store *WalletStore) DeleteRecoveryStatus(recoveryKey []byte) {
422         if store.batch == nil {
423                 store.DB.Delete(recoveryKey)
424         } else {
425                 store.batch.Delete(recoveryKey)
426         }
427 }
428
429 // GetRecoveryStatus delete recovery status
430 func (store *WalletStore) GetRecoveryStatus(recoveryKey []byte) []byte {
431         return store.DB.Get(recoveryKey)
432 }