OSDN Git Service

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