OSDN Git Service

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