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 }
102
103 // ContractUTXOKey makes a smart contract unspent outputs key to store
104 func ContractUTXOKey(id bc.Hash) []byte {
105         name := id.String()
106         return append(SUTXOPrefix, []byte(name)...)
107 }
108
109 func calcDeleteKey(blockHeight uint64) []byte {
110         return []byte(fmt.Sprintf("%s%016x", TxPrefix, blockHeight))
111 }
112
113 func calcTxIndexKey(txID string) []byte {
114         return append(TxIndexPrefix, []byte(txID)...)
115 }
116
117 func calcAnnotatedKey(formatKey string) []byte {
118         return append(TxPrefix, []byte(formatKey)...)
119 }
120
121 func calcUnconfirmedTxKey(formatKey string) []byte {
122         return append(UnconfirmedTxPrefix, []byte(formatKey)...)
123 }
124
125 func calcGlobalTxIndexKey(txID string) []byte {
126         return append(GlobalTxIndexPrefix, []byte(txID)...)
127 }
128
129 func CalcGlobalTxIndex(blockHash *bc.Hash, position uint64) []byte {
130         txIdx := make([]byte, 40)
131         copy(txIdx[:32], blockHash.Bytes())
132         binary.BigEndian.PutUint64(txIdx[32:], position)
133         return txIdx
134 }
135
136 func formatKey(blockHeight uint64, position uint32) string {
137         return fmt.Sprintf("%016x%08x", blockHeight, position)
138 }
139
140 func contractIndexKey(accountID string) []byte {
141         return append([]byte(ContractIndexPrefix), []byte(accountID)...)
142 }
143
144 func accountAliasKey(name string) []byte {
145         return append([]byte(AccountAliasPrefix), []byte(name)...)
146 }
147
148 // WalletStore store wallet using leveldb
149 type WalletStore struct {
150         walletDB dbm.DB
151         batch    dbm.Batch
152 }
153
154 // NewWalletStore create new WalletStore struct
155 func NewWalletStore(db dbm.DB) *WalletStore {
156         return &WalletStore{
157                 walletDB: db,
158                 batch:    nil,
159         }
160 }
161
162 // InitBatch initial batch
163 func (store *WalletStore) InitBatch() error {
164         if store.batch != nil {
165                 return errors.New("WalletStore initail fail, store batch is not nil.")
166         }
167         store.batch = store.walletDB.NewBatch()
168         return nil
169 }
170
171 // CommitBatch commit batch
172 func (store *WalletStore) CommitBatch() error {
173         if store.batch == nil {
174                 return errors.New("WalletStore commit fail, store batch is nil.")
175         }
176         store.batch.Write()
177         store.batch = nil
178         return nil
179 }
180
181 // DeleteContractUTXO delete contract utxo by outputID
182 func (store *WalletStore) DeleteContractUTXO(outputID bc.Hash) {
183         if store.batch == nil {
184                 store.walletDB.Delete(ContractUTXOKey(outputID))
185         } else {
186                 store.batch.Delete(ContractUTXOKey(outputID))
187         }
188 }
189
190 // DeleteRecoveryStatus delete recovery status
191 func (store *WalletStore) DeleteRecoveryStatus() {
192         if store.batch == nil {
193                 store.walletDB.Delete(RecoveryKey)
194         } else {
195                 store.batch.Delete(RecoveryKey)
196         }
197 }
198
199 // DeleteStardardUTXO delete stardard utxo by outputID
200 func (store *WalletStore) DeleteStardardUTXO(outputID bc.Hash) {
201         if store.batch == nil {
202                 store.walletDB.Delete(StandardUTXOKey(outputID))
203         } else {
204                 store.batch.Delete(StandardUTXOKey(outputID))
205         }
206 }
207
208 // DeleteTransactions delete transactions when orphan block rollback
209 func (store *WalletStore) DeleteTransactions(height uint64) {
210         batch := store.walletDB.NewBatch()
211         if store.batch != nil {
212                 batch = store.batch
213         }
214         txIter := store.walletDB.IteratorPrefix(calcDeleteKey(height))
215         defer txIter.Release()
216
217         tmpTx := query.AnnotatedTx{}
218         for txIter.Next() {
219                 if err := json.Unmarshal(txIter.Value(), &tmpTx); err == nil {
220                         batch.Delete(calcTxIndexKey(tmpTx.ID.String()))
221                 }
222                 batch.Delete(txIter.Key())
223         }
224         if store.batch == nil {
225                 batch.Write()
226         }
227 }
228
229 // DeleteUnconfirmedTransaction delete unconfirmed tx by txID
230 func (store *WalletStore) DeleteUnconfirmedTransaction(txID string) {
231         if store.batch == nil {
232                 store.walletDB.Delete(calcUnconfirmedTxKey(txID))
233         } else {
234                 store.batch.Delete(calcUnconfirmedTxKey(txID))
235         }
236 }
237
238 // DeleteWalletTransactions delete all txs in wallet
239 func (store *WalletStore) DeleteWalletTransactions() {
240         batch := store.walletDB.NewBatch()
241         if store.batch != nil {
242                 batch = store.batch
243         }
244         txIter := store.walletDB.IteratorPrefix([]byte(TxPrefix))
245         defer txIter.Release()
246
247         for txIter.Next() {
248                 batch.Delete(txIter.Key())
249         }
250
251         txIndexIter := store.walletDB.IteratorPrefix([]byte(TxIndexPrefix))
252         defer txIndexIter.Release()
253
254         for txIndexIter.Next() {
255                 batch.Delete(txIndexIter.Key())
256         }
257         if store.batch == nil {
258                 batch.Write()
259         }
260 }
261
262 // DeleteWalletUTXOs delete all txs in wallet
263 func (store *WalletStore) DeleteWalletUTXOs() {
264         batch := store.walletDB.NewBatch()
265         if store.batch != nil {
266                 batch = store.batch
267         }
268         ruIter := store.walletDB.IteratorPrefix([]byte(UTXOPrefix))
269         defer ruIter.Release()
270         for ruIter.Next() {
271                 batch.Delete(ruIter.Key())
272         }
273
274         suIter := store.walletDB.IteratorPrefix([]byte(SUTXOPrefix))
275         defer suIter.Release()
276         for suIter.Next() {
277                 batch.Delete(suIter.Key())
278         }
279         if store.batch == nil {
280                 batch.Write()
281         }
282 }
283
284 // GetAccount get account value by account ID
285 func (store *WalletStore) GetAccount(accountID string) (*acc.Account, error) {
286         rawAccount := store.walletDB.Get(AccountIDKey(accountID))
287         if rawAccount == nil {
288                 return nil, fmt.Errorf("failed get account, accountID: %s ", accountID)
289         }
290         account := new(acc.Account)
291         if err := json.Unmarshal(rawAccount, account); err != nil {
292                 return nil, err
293         }
294         return account, nil
295 }
296
297 // GetAsset get asset by assetID
298 func (store *WalletStore) GetAsset(assetID *bc.AssetID) (*asset.Asset, error) {
299         definitionByte := store.walletDB.Get(asset.ExtAssetKey(assetID))
300         if definitionByte == nil {
301                 return nil, errGetAsset
302         }
303         definitionMap := make(map[string]interface{})
304         if err := json.Unmarshal(definitionByte, &definitionMap); err != nil {
305                 return nil, err
306         }
307         alias := assetID.String()
308         externalAsset := &asset.Asset{
309                 AssetID:           *assetID,
310                 Alias:             &alias,
311                 DefinitionMap:     definitionMap,
312                 RawDefinitionByte: definitionByte,
313         }
314         return externalAsset, nil
315 }
316
317 // GetControlProgram get raw program by hash
318 func (store *WalletStore) GetControlProgram(hash bc.Hash) (*acc.CtrlProgram, error) {
319         rawProgram := store.walletDB.Get(ContractKey(hash))
320         if rawProgram == nil {
321                 return nil, fmt.Errorf("failed get account control program:%x ", hash)
322         }
323         accountCP := new(acc.CtrlProgram)
324         if err := json.Unmarshal(rawProgram, &accountCP); err != nil {
325                 return nil, err
326         }
327         return accountCP, nil
328 }
329
330 // GetGlobalTransactionIndex get global tx by txID
331 func (store *WalletStore) GetGlobalTransactionIndex(txID string) []byte {
332         return store.walletDB.Get(calcGlobalTxIndexKey(txID))
333 }
334
335 // GetStandardUTXO get standard utxo by id
336 func (store *WalletStore) GetStandardUTXO(outid bc.Hash) (*acc.UTXO, error) {
337         rawUTXO := store.walletDB.Get(StandardUTXOKey(outid))
338         if rawUTXO == nil {
339                 return nil, fmt.Errorf("failed get standard UTXO, outputID: %s ", outid.String())
340         }
341         UTXO := new(acc.UTXO)
342         if err := json.Unmarshal(rawUTXO, UTXO); err != nil {
343                 return nil, err
344         }
345         return UTXO, nil
346 }
347
348 // GetTransaction get tx by txid
349 func (store *WalletStore) GetTransaction(txID string) (*query.AnnotatedTx, error) {
350         formatKey := store.walletDB.Get(calcTxIndexKey(txID))
351         if formatKey == nil {
352                 return nil, errAccntTxIDNotFound
353         }
354         rawTx := store.walletDB.Get(calcAnnotatedKey(string(formatKey)))
355         tx := new(query.AnnotatedTx)
356         if err := json.Unmarshal(rawTx, tx); err != nil {
357                 return nil, err
358         }
359         return tx, nil
360 }
361
362 // GetUnconfirmedTransaction get unconfirmed tx by txID
363 func (store *WalletStore) GetUnconfirmedTransaction(txID string) (*query.AnnotatedTx, error) {
364         rawUnconfirmedTx := store.walletDB.Get(calcUnconfirmedTxKey(txID))
365         if rawUnconfirmedTx == nil {
366                 return nil, fmt.Errorf("failed get unconfirmed tx, txID: %s ", txID)
367         }
368         tx := new(query.AnnotatedTx)
369         if err := json.Unmarshal(rawUnconfirmedTx, tx); err != nil {
370                 return nil, err
371         }
372         return tx, nil
373 }
374
375 // GetRecoveryStatus delete recovery status
376 func (store *WalletStore) GetRecoveryStatus(recoveryKey []byte) []byte {
377         return store.walletDB.Get(recoveryKey)
378 }
379
380 // GetWalletInfo get wallet information
381 func (store *WalletStore) GetWalletInfo() []byte {
382         return store.walletDB.Get([]byte(WalletKey))
383 }
384
385 // ListAccountUTXOs get all account unspent outputs
386 func (store *WalletStore) ListAccountUTXOs(key string) ([]*acc.UTXO, error) {
387         accountUtxoIter := store.walletDB.IteratorPrefix([]byte(key))
388         defer accountUtxoIter.Release()
389
390         confirmedUTXOs := []*acc.UTXO{}
391         for accountUtxoIter.Next() {
392                 utxo := new(acc.UTXO)
393                 if err := json.Unmarshal(accountUtxoIter.Value(), utxo); err != nil {
394                         return nil, err
395                 }
396                 confirmedUTXOs = append(confirmedUTXOs, utxo)
397         }
398         return confirmedUTXOs, nil
399 }
400
401 func (store *WalletStore) ListTransactions(accountID string, StartTxID string, count uint, unconfirmed bool) ([]*query.AnnotatedTx, error) {
402         annotatedTxs := []*query.AnnotatedTx{}
403         var startKey []byte
404         preFix := TxPrefix
405
406         if StartTxID != "" {
407                 if unconfirmed {
408                         startKey = calcUnconfirmedTxKey(StartTxID)
409                 } else {
410                         formatKey := store.walletDB.Get(calcTxIndexKey(StartTxID))
411                         if formatKey == nil {
412                                 return nil, errAccntTxIDNotFound
413                         }
414                         startKey = calcAnnotatedKey(string(formatKey))
415                 }
416         }
417
418         if unconfirmed {
419                 preFix = UnconfirmedTxPrefix
420         }
421
422         itr := store.walletDB.IteratorPrefixWithStart([]byte(preFix), startKey)
423         defer itr.Release()
424
425         for txNum := count; itr.Next() && txNum > 0; txNum-- {
426                 annotatedTx := new(query.AnnotatedTx)
427                 if err := json.Unmarshal(itr.Value(), &annotatedTx); err != nil {
428                         return nil, err
429                 }
430                 annotatedTxs = append(annotatedTxs, annotatedTx)
431         }
432
433         return annotatedTxs, nil
434 }
435
436 // ListUnconfirmedTransactions get all unconfirmed txs
437 func (store *WalletStore) ListUnconfirmedTransactions() ([]*query.AnnotatedTx, error) {
438         annotatedTxs := []*query.AnnotatedTx{}
439         txIter := store.walletDB.IteratorPrefix([]byte(UnconfirmedTxPrefix))
440         defer txIter.Release()
441
442         for txIter.Next() {
443                 annotatedTx := &query.AnnotatedTx{}
444                 if err := json.Unmarshal(txIter.Value(), &annotatedTx); err != nil {
445                         return nil, err
446                 }
447                 annotatedTxs = append(annotatedTxs, annotatedTx)
448         }
449         return annotatedTxs, nil
450 }
451
452 // SetAssetDefinition set assetID and definition
453 func (store *WalletStore) SetAssetDefinition(assetID *bc.AssetID, definition []byte) {
454         if store.batch == nil {
455                 store.walletDB.Set(asset.ExtAssetKey(assetID), definition)
456         } else {
457                 store.batch.Set(asset.ExtAssetKey(assetID), definition)
458         }
459 }
460
461 // SetContractUTXO set standard utxo
462 func (store *WalletStore) SetContractUTXO(outputID bc.Hash, utxo *acc.UTXO) error {
463         data, err := json.Marshal(utxo)
464         if err != nil {
465                 return err
466         }
467         if store.batch == nil {
468                 store.walletDB.Set(ContractUTXOKey(outputID), data)
469         } else {
470                 store.batch.Set(ContractUTXOKey(outputID), data)
471         }
472         return nil
473 }
474
475 // SetGlobalTransactionIndex set global tx index by blockhash and position
476 func (store *WalletStore) SetGlobalTransactionIndex(globalTxID string, blockHash *bc.Hash, position uint64) {
477         if store.batch == nil {
478                 store.walletDB.Set(calcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
479         } else {
480                 store.batch.Set(calcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
481         }
482 }
483
484 // SetRecoveryStatus set recovery status
485 func (store *WalletStore) SetRecoveryStatus(recoveryKey, rawStatus []byte) {
486         if store.batch == nil {
487                 store.walletDB.Set(recoveryKey, rawStatus)
488         } else {
489                 store.batch.Set(recoveryKey, rawStatus)
490         }
491 }
492
493 // SetStandardUTXO set standard utxo
494 func (store *WalletStore) SetStandardUTXO(outputID bc.Hash, utxo *acc.UTXO) error {
495         data, err := json.Marshal(utxo)
496         if err != nil {
497                 return err
498         }
499         if store.batch == nil {
500                 store.walletDB.Set(StandardUTXOKey(outputID), data)
501         } else {
502                 store.batch.Set(StandardUTXOKey(outputID), data)
503         }
504         return nil
505 }
506
507 // SetTransaction set raw transaction by block height and tx position
508 func (store *WalletStore) SetTransaction(height uint64, tx *query.AnnotatedTx) error {
509         batch := store.walletDB.NewBatch()
510         if store.batch != nil {
511                 batch = store.batch
512         }
513
514         rawTx, err := json.Marshal(tx)
515         if err != nil {
516                 return err
517         }
518         batch.Set(calcAnnotatedKey(formatKey(height, tx.Position)), rawTx)
519         batch.Set(calcTxIndexKey(tx.ID.String()), []byte(formatKey(height, tx.Position)))
520
521         if store.batch == nil {
522                 batch.Write()
523         }
524         return nil
525 }
526
527 // SetUnconfirmedTransaction set unconfirmed tx by txID
528 func (store *WalletStore) SetUnconfirmedTransaction(txID string, tx *query.AnnotatedTx) error {
529         rawTx, err := json.Marshal(tx)
530         if err != nil {
531                 return err
532         }
533         if store.batch == nil {
534                 store.walletDB.Set(calcUnconfirmedTxKey(txID), rawTx)
535         } else {
536                 store.batch.Set(calcUnconfirmedTxKey(txID), rawTx)
537         }
538         return nil
539 }
540
541 // SetWalletInfo get wallet information
542 func (store *WalletStore) SetWalletInfo(rawWallet []byte) {
543         if store.batch == nil {
544                 store.walletDB.Set([]byte(WalletKey), rawWallet)
545         } else {
546                 store.batch.Set([]byte(WalletKey), rawWallet)
547         }
548 }