OSDN Git Service

fix the bug (#372)
[bytom/vapor.git] / database / account_store.go
1 package database
2
3 import (
4         "encoding/json"
5         "sort"
6         "strings"
7
8         acc "github.com/vapor/account"
9         "github.com/vapor/blockchain/signers"
10         "github.com/vapor/common"
11         "github.com/vapor/crypto/ed25519/chainkd"
12         "github.com/vapor/crypto/sha3pool"
13         dbm "github.com/vapor/database/leveldb"
14         "github.com/vapor/errors"
15         "github.com/vapor/protocol/bc"
16 )
17
18 const (
19         utxoPrefix byte = iota //UTXOPrefix is StandardUTXOKey prefix
20         contractPrefix
21         contractIndexPrefix
22         accountPrefix // AccountPrefix is account ID prefix
23         accountIndexPrefix
24 )
25
26 // leveldb key prefix
27 var (
28         accountStore        = []byte("AS:")
29         UTXOPrefix          = append(accountStore, utxoPrefix, colon)
30         ContractPrefix      = append(accountStore, contractPrefix, colon)
31         ContractIndexPrefix = append(accountStore, contractIndexPrefix, colon)
32         AccountPrefix       = append(accountStore, accountPrefix, colon) // AccountPrefix is account ID prefix
33         AccountIndexPrefix  = append(accountStore, accountIndexPrefix, colon)
34 )
35
36 func accountIndexKey(xpubs []chainkd.XPub) []byte {
37         var hash [32]byte
38         var xPubs []byte
39         cpy := append([]chainkd.XPub{}, xpubs[:]...)
40         sort.Sort(signers.SortKeys(cpy))
41         for _, xpub := range cpy {
42                 xPubs = append(xPubs, xpub[:]...)
43         }
44         sha3pool.Sum256(hash[:], xPubs)
45         return append(AccountIndexPrefix, hash[:]...)
46 }
47
48 func Bip44ContractIndexKey(accountID string, change bool) []byte {
49         key := append(ContractIndexPrefix, []byte(accountID)...)
50         if change {
51                 return append(key, 0x01)
52         }
53         return append(key, 0x00)
54 }
55
56 // ContractKey account control promgram store prefix
57 func ContractKey(hash bc.Hash) []byte {
58         return append(ContractPrefix, hash.Bytes()...)
59 }
60
61 // AccountIDKey account id store prefix
62 func AccountIDKey(accountID string) []byte {
63         return append(AccountPrefix, []byte(accountID)...)
64 }
65
66 // StandardUTXOKey makes an account unspent outputs key to store
67 func StandardUTXOKey(id bc.Hash) []byte {
68         return append(UTXOPrefix, id.Bytes()...)
69 }
70
71 func accountAliasKey(name string) []byte {
72         return append(AccountAliasPrefix, []byte(name)...)
73 }
74
75 // AccountStore satisfies AccountStore interface.
76 type AccountStore struct {
77         db    dbm.DB
78         batch dbm.Batch
79 }
80
81 // NewAccountStore create new AccountStore.
82 func NewAccountStore(db dbm.DB) *AccountStore {
83         return &AccountStore{
84                 db:    db,
85                 batch: nil,
86         }
87 }
88
89 // InitBatch initial new account store
90 func (store *AccountStore) InitBatch() acc.AccountStore {
91         newStore := NewAccountStore(store.db)
92         newStore.batch = newStore.db.NewBatch()
93         return newStore
94 }
95
96 // CommitBatch commit batch
97 func (store *AccountStore) CommitBatch() error {
98         if store.batch == nil {
99                 return errors.New("AccountStore commit fail, store batch is nil.")
100         }
101         store.batch.Write()
102         store.batch = nil
103         return nil
104 }
105
106 // DeleteAccount set account account ID, account alias and raw account.
107 func (store *AccountStore) DeleteAccount(account *acc.Account) error {
108         batch := store.db.NewBatch()
109         if store.batch != nil {
110                 batch = store.batch
111         }
112
113         // delete account utxos
114         store.deleteAccountUTXOs(account.ID, batch)
115
116         // delete account control program
117         if err := store.deleteAccountControlPrograms(account.ID, batch); err != nil {
118                 return err
119         }
120
121         // delete bip44 contract index
122         batch.Delete(Bip44ContractIndexKey(account.ID, false))
123         batch.Delete(Bip44ContractIndexKey(account.ID, true))
124
125         // delete contract index
126         batch.Delete(contractIndexKey(account.ID))
127
128         // delete account id
129         batch.Delete(AccountIDKey(account.ID))
130         batch.Delete(accountAliasKey(account.Alias))
131         if store.batch == nil {
132                 batch.Write()
133         }
134         return nil
135 }
136
137 // deleteAccountUTXOs delete account utxos by accountID
138 func (store *AccountStore) deleteAccountUTXOs(accountID string, batch dbm.Batch) error {
139         accountUtxoIter := store.db.IteratorPrefix(UTXOPrefix)
140         defer accountUtxoIter.Release()
141
142         for accountUtxoIter.Next() {
143                 accountUtxo := new(acc.UTXO)
144                 if err := json.Unmarshal(accountUtxoIter.Value(), accountUtxo); err != nil {
145                         return err
146                 }
147
148                 if accountID == accountUtxo.AccountID {
149                         batch.Delete(StandardUTXOKey(accountUtxo.OutputID))
150                 }
151         }
152
153         return nil
154 }
155
156 // deleteAccountControlPrograms deletes account control program
157 func (store *AccountStore) deleteAccountControlPrograms(accountID string, batch dbm.Batch) error {
158         cps, err := store.ListControlPrograms()
159         if err != nil {
160                 return err
161         }
162
163         var hash [32]byte
164         for _, cp := range cps {
165                 if cp.AccountID == accountID {
166                         sha3pool.Sum256(hash[:], cp.ControlProgram)
167                         batch.Delete(ContractKey(bc.NewHash(hash)))
168                 }
169         }
170         return nil
171 }
172
173 // DeleteStandardUTXO delete utxo by outpu id
174 func (store *AccountStore) DeleteStandardUTXO(outputID bc.Hash) {
175         if store.batch == nil {
176                 store.db.Delete(StandardUTXOKey(outputID))
177         } else {
178                 store.batch.Delete(StandardUTXOKey(outputID))
179         }
180 }
181
182 // GetAccountByAlias get account by account alias
183 func (store *AccountStore) GetAccountByAlias(accountAlias string) (*acc.Account, error) {
184         accountID := store.db.Get(accountAliasKey(accountAlias))
185         if accountID == nil {
186                 return nil, acc.ErrFindAccount
187         }
188
189         return store.GetAccountByID(string(accountID))
190 }
191
192 // GetAccountByID get account by accountID
193 func (store *AccountStore) GetAccountByID(accountID string) (*acc.Account, error) {
194         rawAccount := store.db.Get(AccountIDKey(accountID))
195         if rawAccount == nil {
196                 return nil, acc.ErrFindAccount
197         }
198
199         account := new(acc.Account)
200         if err := json.Unmarshal(rawAccount, account); err != nil {
201                 return nil, err
202         }
203
204         return account, nil
205 }
206
207 // GetAccountIndex get account index by account xpubs
208 func (store *AccountStore) GetAccountIndex(xpubs []chainkd.XPub) uint64 {
209         currentIndex := uint64(0)
210         if rawIndexBytes := store.db.Get(accountIndexKey(xpubs)); rawIndexBytes != nil {
211                 currentIndex = common.BytesToUnit64(rawIndexBytes)
212         }
213         return currentIndex
214 }
215
216 // GetBip44ContractIndex get bip44 contract index
217 func (store *AccountStore) GetBip44ContractIndex(accountID string, change bool) uint64 {
218         index := uint64(0)
219         if rawIndexBytes := store.db.Get(Bip44ContractIndexKey(accountID, change)); rawIndexBytes != nil {
220                 index = common.BytesToUnit64(rawIndexBytes)
221         }
222         return index
223 }
224
225 // GetCoinbaseArbitrary get coinbase arbitrary
226 func (store *AccountStore) GetCoinbaseArbitrary() []byte {
227         return store.db.Get(CoinbaseAbKey)
228 }
229
230 // GetContractIndex get contract index
231 func (store *AccountStore) GetContractIndex(accountID string) uint64 {
232         index := uint64(0)
233         if rawIndexBytes := store.db.Get(contractIndexKey(accountID)); rawIndexBytes != nil {
234                 index = common.BytesToUnit64(rawIndexBytes)
235         }
236         return index
237 }
238
239 // GetControlProgram get control program
240 func (store *AccountStore) GetControlProgram(hash bc.Hash) (*acc.CtrlProgram, error) {
241         rawProgram := store.db.Get(ContractKey(hash))
242         if rawProgram == nil {
243                 return nil, acc.ErrFindCtrlProgram
244         }
245
246         cp := new(acc.CtrlProgram)
247         if err := json.Unmarshal(rawProgram, cp); err != nil {
248                 return nil, err
249         }
250
251         return cp, nil
252 }
253
254 // GetMiningAddress get mining address
255 func (store *AccountStore) GetMiningAddress() (*acc.CtrlProgram, error) {
256         rawCP := store.db.Get(MiningAddressKey)
257         if rawCP == nil {
258                 return nil, acc.ErrFindMiningAddress
259         }
260
261         cp := new(acc.CtrlProgram)
262         if err := json.Unmarshal(rawCP, cp); err != nil {
263                 return nil, err
264         }
265
266         return cp, nil
267 }
268
269 // GetUTXO get standard utxo by id
270 func (store *AccountStore) GetUTXO(outid bc.Hash) (*acc.UTXO, error) {
271         u := new(acc.UTXO)
272         if data := store.db.Get(StandardUTXOKey(outid)); data != nil {
273                 return u, json.Unmarshal(data, u)
274         }
275
276         if data := store.db.Get(ContractUTXOKey(outid)); data != nil {
277                 return u, json.Unmarshal(data, u)
278         }
279
280         return nil, acc.ErrMatchUTXO
281 }
282
283 // ListAccounts get all accounts which name prfix is id.
284 func (store *AccountStore) ListAccounts(id string) ([]*acc.Account, error) {
285         accounts := []*acc.Account{}
286         accountIter := store.db.IteratorPrefix(AccountIDKey(strings.TrimSpace(id)))
287         defer accountIter.Release()
288
289         for accountIter.Next() {
290                 account := new(acc.Account)
291                 if err := json.Unmarshal(accountIter.Value(), account); err != nil {
292                         return nil, err
293                 }
294
295                 accounts = append(accounts, account)
296         }
297         return accounts, nil
298 }
299
300 // ListControlPrograms get all local control programs
301 func (store *AccountStore) ListControlPrograms() ([]*acc.CtrlProgram, error) {
302         cps := []*acc.CtrlProgram{}
303         cpIter := store.db.IteratorPrefix(ContractPrefix)
304         defer cpIter.Release()
305
306         for cpIter.Next() {
307                 cp := new(acc.CtrlProgram)
308                 if err := json.Unmarshal(cpIter.Value(), cp); err != nil {
309                         return nil, err
310                 }
311
312                 cps = append(cps, cp)
313         }
314         return cps, nil
315 }
316
317 // ListUTXOs list all utxos
318 func (store *AccountStore) ListUTXOs() ([]*acc.UTXO, error) {
319         utxoIter := store.db.IteratorPrefix(UTXOPrefix)
320         defer utxoIter.Release()
321
322         utxos := []*acc.UTXO{}
323         for utxoIter.Next() {
324                 utxo := new(acc.UTXO)
325                 if err := json.Unmarshal(utxoIter.Value(), utxo); err != nil {
326                         return nil, err
327                 }
328
329                 utxos = append(utxos, utxo)
330         }
331         return utxos, nil
332 }
333
334 // SetAccount set account account ID, account alias and raw account.
335 func (store *AccountStore) SetAccount(account *acc.Account) error {
336         rawAccount, err := json.Marshal(account)
337         if err != nil {
338                 return acc.ErrMarshalAccount
339         }
340
341         batch := store.db.NewBatch()
342         if store.batch != nil {
343                 batch = store.batch
344         }
345
346         batch.Set(AccountIDKey(account.ID), rawAccount)
347         batch.Set(accountAliasKey(account.Alias), []byte(account.ID))
348
349         if store.batch == nil {
350                 batch.Write()
351         }
352         return nil
353 }
354
355 // SetAccountIndex update account index
356 func (store *AccountStore) SetAccountIndex(account *acc.Account) {
357         currentIndex := store.GetAccountIndex(account.XPubs)
358         if account.KeyIndex > currentIndex {
359                 if store.batch == nil {
360                         store.db.Set(accountIndexKey(account.XPubs), common.Unit64ToBytes(account.KeyIndex))
361                 } else {
362                         store.batch.Set(accountIndexKey(account.XPubs), common.Unit64ToBytes(account.KeyIndex))
363                 }
364         }
365 }
366
367 // SetBip44ContractIndex set contract index
368 func (store *AccountStore) SetBip44ContractIndex(accountID string, change bool, index uint64) {
369         if store.batch == nil {
370                 store.db.Set(Bip44ContractIndexKey(accountID, change), common.Unit64ToBytes(index))
371         } else {
372                 store.batch.Set(Bip44ContractIndexKey(accountID, change), common.Unit64ToBytes(index))
373         }
374 }
375
376 // SetCoinbaseArbitrary set coinbase arbitrary
377 func (store *AccountStore) SetCoinbaseArbitrary(arbitrary []byte) {
378         if store.batch == nil {
379                 store.db.Set(CoinbaseAbKey, arbitrary)
380         } else {
381                 store.batch.Set(CoinbaseAbKey, arbitrary)
382         }
383 }
384
385 // SetContractIndex set contract index
386 func (store *AccountStore) SetContractIndex(accountID string, index uint64) {
387         if store.batch == nil {
388                 store.db.Set(contractIndexKey(accountID), common.Unit64ToBytes(index))
389         } else {
390                 store.batch.Set(contractIndexKey(accountID), common.Unit64ToBytes(index))
391         }
392 }
393
394 // SetControlProgram set raw program
395 func (store *AccountStore) SetControlProgram(hash bc.Hash, program *acc.CtrlProgram) error {
396         accountCP, err := json.Marshal(program)
397         if err != nil {
398                 return err
399         }
400         if store.batch == nil {
401                 store.db.Set(ContractKey(hash), accountCP)
402         } else {
403                 store.batch.Set(ContractKey(hash), accountCP)
404         }
405         return nil
406 }
407
408 // SetMiningAddress set mining address
409 func (store *AccountStore) SetMiningAddress(program *acc.CtrlProgram) error {
410         rawProgram, err := json.Marshal(program)
411         if err != nil {
412                 return err
413         }
414
415         if store.batch == nil {
416                 store.db.Set(MiningAddressKey, rawProgram)
417         } else {
418                 store.batch.Set(MiningAddressKey, rawProgram)
419         }
420         return nil
421 }
422
423 // SetStandardUTXO set standard utxo
424 func (store *AccountStore) SetStandardUTXO(outputID bc.Hash, utxo *acc.UTXO) error {
425         data, err := json.Marshal(utxo)
426         if err != nil {
427                 return err
428         }
429
430         if store.batch == nil {
431                 store.db.Set(StandardUTXOKey(outputID), data)
432         } else {
433                 store.batch.Set(StandardUTXOKey(outputID), data)
434         }
435         return nil
436 }