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"
19 utxoPrefix byte = iota //UTXOPrefix is StandardUTXOKey prefix
22 accountPrefix // AccountPrefix is account ID prefix
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)
36 func accountIndexKey(xpubs []chainkd.XPub) []byte {
39 cpy := append([]chainkd.XPub{}, xpubs[:]...)
40 sort.Sort(signers.SortKeys(cpy))
41 for _, xpub := range cpy {
42 xPubs = append(xPubs, xpub[:]...)
44 sha3pool.Sum256(hash[:], xPubs)
45 return append(AccountIndexPrefix, hash[:]...)
48 func Bip44ContractIndexKey(accountID string, change bool) []byte {
49 key := append(ContractIndexPrefix, []byte(accountID)...)
51 return append(key, 0x01)
53 return append(key, 0x00)
56 // ContractKey account control promgram store prefix
57 func ContractKey(hash bc.Hash) []byte {
58 return append(ContractPrefix, hash.Bytes()...)
61 // AccountIDKey account id store prefix
62 func AccountIDKey(accountID string) []byte {
63 return append(AccountPrefix, []byte(accountID)...)
66 // StandardUTXOKey makes an account unspent outputs key to store
67 func StandardUTXOKey(id bc.Hash) []byte {
68 return append(UTXOPrefix, id.Bytes()...)
71 // AccountStore satisfies AccountStore interface.
72 type AccountStore struct {
77 // NewAccountStore create new AccountStore.
78 func NewAccountStore(db dbm.DB) *AccountStore {
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.")
90 store.batch = store.db.NewBatch()
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.")
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 {
111 // delete account utxos
112 store.deleteAccountUTXOs(account.ID, batch)
114 // delete account control program
115 if err := store.deleteAccountControlPrograms(account.ID, batch); err != nil {
119 // delete bip44 contract index
120 batch.Delete(Bip44ContractIndexKey(account.ID, false))
121 batch.Delete(Bip44ContractIndexKey(account.ID, true))
123 // delete contract index
124 batch.Delete(contractIndexKey(account.ID))
127 batch.Delete(AccountIDKey(account.ID))
128 batch.Delete(accountAliasKey(account.Alias))
129 if store.batch == nil {
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()
140 for accountUtxoIter.Next() {
141 accountUtxo := new(acc.UTXO)
142 if err := json.Unmarshal(accountUtxoIter.Value(), accountUtxo); err != nil {
146 if accountID == accountUtxo.AccountID {
147 batch.Delete(StandardUTXOKey(accountUtxo.OutputID))
154 // deleteAccountControlPrograms deletes account control program
155 func (store *AccountStore) deleteAccountControlPrograms(accountID string, batch dbm.Batch) error {
156 cps, err := store.ListControlPrograms()
162 for _, cp := range cps {
163 if cp.AccountID == accountID {
164 sha3pool.Sum256(hash[:], cp.ControlProgram)
165 batch.Delete(ContractKey(bc.NewHash(hash)))
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))
176 store.batch.Delete(StandardUTXOKey(outputID))
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
186 return store.GetAccountByID(string(accountID))
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
196 account := new(acc.Account)
197 if err := json.Unmarshal(rawAccount, account); err != nil {
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)
213 // GetBip44ContractIndex get bip44 contract index
214 func (store *AccountStore) GetBip44ContractIndex(accountID string, change bool) uint64 {
216 if rawIndexBytes := store.db.Get(Bip44ContractIndexKey(accountID, change)); rawIndexBytes != nil {
217 index = common.BytesToUnit64(rawIndexBytes)
222 // GetCoinbaseArbitrary get coinbase arbitrary
223 func (store *AccountStore) GetCoinbaseArbitrary() []byte {
224 return store.db.Get(CoinbaseAbKey)
227 // GetContractIndex get contract index
228 func (store *AccountStore) GetContractIndex(accountID string) uint64 {
230 if rawIndexBytes := store.db.Get(contractIndexKey(accountID)); rawIndexBytes != nil {
231 index = common.BytesToUnit64(rawIndexBytes)
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
243 cp := new(acc.CtrlProgram)
244 if err := json.Unmarshal(rawProgram, cp); err != nil {
251 // GetMiningAddress get mining address
252 func (store *AccountStore) GetMiningAddress() (*acc.CtrlProgram, error) {
253 rawCP := store.db.Get(MiningAddressKey)
255 return nil, acc.ErrFindMiningAddress
258 cp := new(acc.CtrlProgram)
259 if err := json.Unmarshal(rawCP, cp); err != nil {
266 // GetUTXO get standard utxo by id
267 func (store *AccountStore) GetUTXO(outid bc.Hash) (*acc.UTXO, error) {
269 if data := store.db.Get(StandardUTXOKey(outid)); data != nil {
270 return u, json.Unmarshal(data, u)
273 if data := store.db.Get(ContractUTXOKey(outid)); data != nil {
274 return u, json.Unmarshal(data, u)
277 return nil, acc.ErrMatchUTXO
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()
286 for accountIter.Next() {
287 account := new(acc.Account)
288 if err := json.Unmarshal(accountIter.Value(), account); err != nil {
292 accounts = append(accounts, account)
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()
304 cp := new(acc.CtrlProgram)
305 if err := json.Unmarshal(cpIter.Value(), cp); err != nil {
309 cps = append(cps, cp)
314 // ListUTXOs get utxos by accountID
315 func (store *AccountStore) ListUTXOs() ([]*acc.UTXO, error) {
316 utxoIter := store.db.IteratorPrefix(UTXOPrefix)
317 defer utxoIter.Release()
319 utxos := []*acc.UTXO{}
320 for utxoIter.Next() {
321 utxo := new(acc.UTXO)
322 if err := json.Unmarshal(utxoIter.Value(), utxo); err != nil {
326 utxos = append(utxos, utxo)
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)
335 return acc.ErrMarshalAccount
338 batch := store.db.NewBatch()
339 if store.batch != nil {
343 batch.Set(AccountIDKey(account.ID), rawAccount)
344 batch.Set(accountAliasKey(account.Alias), []byte(account.ID))
346 if store.batch == nil {
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))
359 store.batch.Set(accountIndexKey(account.XPubs), common.Unit64ToBytes(account.KeyIndex))
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))
369 store.batch.Set(Bip44ContractIndexKey(accountID, change), common.Unit64ToBytes(index))
373 // SetCoinbaseArbitrary set coinbase arbitrary
374 func (store *AccountStore) SetCoinbaseArbitrary(arbitrary []byte) {
375 if store.batch == nil {
376 store.db.Set(CoinbaseAbKey, arbitrary)
378 store.batch.Set(CoinbaseAbKey, arbitrary)
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))
387 store.batch.Set(contractIndexKey(accountID), common.Unit64ToBytes(index))
391 // SetControlProgram set raw program
392 func (store *AccountStore) SetControlProgram(hash bc.Hash, program *acc.CtrlProgram) error {
393 accountCP, err := json.Marshal(program)
397 if store.batch == nil {
398 store.db.Set(ContractKey(hash), accountCP)
400 store.batch.Set(ContractKey(hash), accountCP)
405 // SetMiningAddress set mining address
406 func (store *AccountStore) SetMiningAddress(program *acc.CtrlProgram) error {
407 rawProgram, err := json.Marshal(program)
412 if store.batch == nil {
413 store.db.Set(MiningAddressKey, rawProgram)
415 store.batch.Set(MiningAddressKey, rawProgram)
420 // SetStandardUTXO set standard utxo
421 func (store *AccountStore) SetStandardUTXO(outputID bc.Hash, utxo *acc.UTXO) error {
422 data, err := json.Marshal(utxo)
427 if store.batch == nil {
428 store.db.Set(StandardUTXOKey(outputID), data)
430 store.batch.Set(StandardUTXOKey(outputID), data)