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