OSDN Git Service

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