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 // InitStore initial new account store
86 func (store *AccountStore) InitStore() acc.AccountStore {
87 newStore := NewAccountStore(store.db)
88 newStore.batch = newStore.db.NewBatch()
92 // CommitBatch commit batch
93 func (store *AccountStore) CommitBatch() error {
94 if store.batch == nil {
95 return errors.New("AccountStore commit fail, store batch is nil.")
102 // DeleteAccount set account account ID, account alias and raw account.
103 func (store *AccountStore) DeleteAccount(account *acc.Account) error {
104 batch := store.db.NewBatch()
105 if store.batch != nil {
109 // delete account utxos
110 store.deleteAccountUTXOs(account.ID, batch)
112 // delete account control program
113 if err := store.deleteAccountControlPrograms(account.ID, batch); err != nil {
117 // delete bip44 contract index
118 batch.Delete(Bip44ContractIndexKey(account.ID, false))
119 batch.Delete(Bip44ContractIndexKey(account.ID, true))
121 // delete contract index
122 batch.Delete(contractIndexKey(account.ID))
125 batch.Delete(AccountIDKey(account.ID))
126 batch.Delete(accountAliasKey(account.Alias))
127 if store.batch == nil {
133 // deleteAccountUTXOs delete account utxos by accountID
134 func (store *AccountStore) deleteAccountUTXOs(accountID string, batch dbm.Batch) error {
135 accountUtxoIter := store.db.IteratorPrefix(UTXOPrefix)
136 defer accountUtxoIter.Release()
138 for accountUtxoIter.Next() {
139 accountUtxo := new(acc.UTXO)
140 if err := json.Unmarshal(accountUtxoIter.Value(), accountUtxo); err != nil {
144 if accountID == accountUtxo.AccountID {
145 batch.Delete(StandardUTXOKey(accountUtxo.OutputID))
152 // deleteAccountControlPrograms deletes account control program
153 func (store *AccountStore) deleteAccountControlPrograms(accountID string, batch dbm.Batch) error {
154 cps, err := store.ListControlPrograms()
160 for _, cp := range cps {
161 if cp.AccountID == accountID {
162 sha3pool.Sum256(hash[:], cp.ControlProgram)
163 batch.Delete(ContractKey(bc.NewHash(hash)))
169 // DeleteStandardUTXO delete utxo by outpu id
170 func (store *AccountStore) DeleteStandardUTXO(outputID bc.Hash) {
171 if store.batch == nil {
172 store.db.Delete(StandardUTXOKey(outputID))
174 store.batch.Delete(StandardUTXOKey(outputID))
178 // GetAccountByAlias get account by account alias
179 func (store *AccountStore) GetAccountByAlias(accountAlias string) (*acc.Account, error) {
180 accountID := store.db.Get(accountAliasKey(accountAlias))
181 if accountID == nil {
182 return nil, acc.ErrFindAccount
184 return store.GetAccountByID(string(accountID))
187 // GetAccountByID get account by accountID
188 func (store *AccountStore) GetAccountByID(accountID string) (*acc.Account, error) {
189 rawAccount := store.db.Get(AccountIDKey(accountID))
190 if rawAccount == nil {
191 return nil, acc.ErrFindAccount
194 account := new(acc.Account)
195 if err := json.Unmarshal(rawAccount, account); err != nil {
202 // GetAccountIndex get account index by account xpubs
203 func (store *AccountStore) GetAccountIndex(xpubs []chainkd.XPub) uint64 {
204 currentIndex := uint64(0)
205 if rawIndexBytes := store.db.Get(accountIndexKey(xpubs)); rawIndexBytes != nil {
206 currentIndex = common.BytesToUnit64(rawIndexBytes)
211 // GetBip44ContractIndex get bip44 contract index
212 func (store *AccountStore) GetBip44ContractIndex(accountID string, change bool) uint64 {
214 if rawIndexBytes := store.db.Get(Bip44ContractIndexKey(accountID, change)); rawIndexBytes != nil {
215 index = common.BytesToUnit64(rawIndexBytes)
220 // GetCoinbaseArbitrary get coinbase arbitrary
221 func (store *AccountStore) GetCoinbaseArbitrary() []byte {
222 return store.db.Get(CoinbaseAbKey)
225 // GetContractIndex get contract index
226 func (store *AccountStore) GetContractIndex(accountID string) uint64 {
228 if rawIndexBytes := store.db.Get(contractIndexKey(accountID)); rawIndexBytes != nil {
229 index = common.BytesToUnit64(rawIndexBytes)
234 // GetControlProgram get control program
235 func (store *AccountStore) GetControlProgram(hash bc.Hash) (*acc.CtrlProgram, error) {
236 rawProgram := store.db.Get(ContractKey(hash))
237 if rawProgram == nil {
238 return nil, acc.ErrFindCtrlProgram
241 cp := new(acc.CtrlProgram)
242 if err := json.Unmarshal(rawProgram, cp); err != nil {
249 // GetMiningAddress get mining address
250 func (store *AccountStore) GetMiningAddress() (*acc.CtrlProgram, error) {
251 rawCP := store.db.Get(MiningAddressKey)
253 return nil, acc.ErrFindMiningAddress
256 cp := new(acc.CtrlProgram)
257 if err := json.Unmarshal(rawCP, cp); err != nil {
264 // GetUTXO get standard utxo by id
265 func (store *AccountStore) GetUTXO(outid bc.Hash) (*acc.UTXO, error) {
267 if data := store.db.Get(StandardUTXOKey(outid)); data != nil {
268 return u, json.Unmarshal(data, u)
271 if data := store.db.Get(ContractUTXOKey(outid)); data != nil {
272 return u, json.Unmarshal(data, u)
275 return nil, acc.ErrMatchUTXO
278 // ListAccounts get all accounts which name prfix is id.
279 func (store *AccountStore) ListAccounts(id string) ([]*acc.Account, error) {
280 accounts := []*acc.Account{}
281 accountIter := store.db.IteratorPrefix(AccountIDKey(strings.TrimSpace(id)))
282 defer accountIter.Release()
284 for accountIter.Next() {
285 account := new(acc.Account)
286 if err := json.Unmarshal(accountIter.Value(), account); err != nil {
290 accounts = append(accounts, account)
295 // ListControlPrograms get all local control programs
296 func (store *AccountStore) ListControlPrograms() ([]*acc.CtrlProgram, error) {
297 cps := []*acc.CtrlProgram{}
298 cpIter := store.db.IteratorPrefix(ContractPrefix)
299 defer cpIter.Release()
302 cp := new(acc.CtrlProgram)
303 if err := json.Unmarshal(cpIter.Value(), cp); err != nil {
307 cps = append(cps, cp)
312 // ListUTXOs get utxos by accountID
313 func (store *AccountStore) ListUTXOs() ([]*acc.UTXO, error) {
314 utxoIter := store.db.IteratorPrefix(UTXOPrefix)
315 defer utxoIter.Release()
317 utxos := []*acc.UTXO{}
318 for utxoIter.Next() {
319 utxo := new(acc.UTXO)
320 if err := json.Unmarshal(utxoIter.Value(), utxo); err != nil {
324 utxos = append(utxos, utxo)
329 // SetAccount set account account ID, account alias and raw account.
330 func (store *AccountStore) SetAccount(account *acc.Account) error {
331 rawAccount, err := json.Marshal(account)
333 return acc.ErrMarshalAccount
336 batch := store.db.NewBatch()
337 if store.batch != nil {
341 batch.Set(AccountIDKey(account.ID), rawAccount)
342 batch.Set(accountAliasKey(account.Alias), []byte(account.ID))
344 if store.batch == nil {
350 // SetAccountIndex update account index
351 func (store *AccountStore) SetAccountIndex(account *acc.Account) {
352 currentIndex := store.GetAccountIndex(account.XPubs)
353 if account.KeyIndex > currentIndex {
354 if store.batch == nil {
355 store.db.Set(accountIndexKey(account.XPubs), common.Unit64ToBytes(account.KeyIndex))
357 store.batch.Set(accountIndexKey(account.XPubs), common.Unit64ToBytes(account.KeyIndex))
362 // SetBip44ContractIndex set contract index
363 func (store *AccountStore) SetBip44ContractIndex(accountID string, change bool, index uint64) {
364 if store.batch == nil {
365 store.db.Set(Bip44ContractIndexKey(accountID, change), common.Unit64ToBytes(index))
367 store.batch.Set(Bip44ContractIndexKey(accountID, change), common.Unit64ToBytes(index))
371 // SetCoinbaseArbitrary set coinbase arbitrary
372 func (store *AccountStore) SetCoinbaseArbitrary(arbitrary []byte) {
373 if store.batch == nil {
374 store.db.Set(CoinbaseAbKey, arbitrary)
376 store.batch.Set(CoinbaseAbKey, arbitrary)
380 // SetContractIndex set contract index
381 func (store *AccountStore) SetContractIndex(accountID string, index uint64) {
382 if store.batch == nil {
383 store.db.Set(contractIndexKey(accountID), common.Unit64ToBytes(index))
385 store.batch.Set(contractIndexKey(accountID), common.Unit64ToBytes(index))
389 // SetControlProgram set raw program
390 func (store *AccountStore) SetControlProgram(hash bc.Hash, program *acc.CtrlProgram) error {
391 accountCP, err := json.Marshal(program)
395 if store.batch == nil {
396 store.db.Set(ContractKey(hash), accountCP)
398 store.batch.Set(ContractKey(hash), accountCP)
403 // SetMiningAddress set mining address
404 func (store *AccountStore) SetMiningAddress(program *acc.CtrlProgram) error {
405 rawProgram, err := json.Marshal(program)
410 if store.batch == nil {
411 store.db.Set(MiningAddressKey, rawProgram)
413 store.batch.Set(MiningAddressKey, rawProgram)
418 // SetStandardUTXO set standard utxo
419 func (store *AccountStore) SetStandardUTXO(outputID bc.Hash, utxo *acc.UTXO) error {
420 data, err := json.Marshal(utxo)
425 if store.batch == nil {
426 store.db.Set(StandardUTXOKey(outputID), data)
428 store.batch.Set(StandardUTXOKey(outputID), data)