OSDN Git Service

update
[bytom/vapor.git] / database / account_store.go
index 2f4aca7..a02f17a 100644 (file)
@@ -2,11 +2,11 @@ package database
 
 import (
        "encoding/json"
-       "fmt"
+       "sort"
        "strings"
 
-       log "github.com/sirupsen/logrus"
        acc "github.com/vapor/account"
+       "github.com/vapor/blockchain/signers"
        "github.com/vapor/common"
        "github.com/vapor/crypto/ed25519/chainkd"
        "github.com/vapor/crypto/sha3pool"
@@ -15,31 +15,82 @@ import (
        "github.com/vapor/protocol/bc"
 )
 
-// AccountStore satisfies AccountStorer interface.
+const (
+       utxoPrefix byte = iota //UTXOPrefix is StandardUTXOKey prefix
+       contractPrefix
+       contractIndexPrefix
+       accountPrefix // AccountPrefix is account ID prefix
+       accountIndexPrefix
+)
+
+// leveldb key prefix
+var (
+       accountStore        = []byte("AS:")
+       UTXOPrefix          = append(accountStore, utxoPrefix, colon)
+       ContractPrefix      = append(accountStore, contractPrefix, colon)
+       ContractIndexPrefix = append(accountStore, contractIndexPrefix, colon)
+       AccountPrefix       = append(accountStore, accountPrefix, colon) // AccountPrefix is account ID prefix
+       AccountIndexPrefix  = append(accountStore, accountIndexPrefix, colon)
+)
+
+func accountIndexKey(xpubs []chainkd.XPub) []byte {
+       var hash [32]byte
+       var xPubs []byte
+       cpy := append([]chainkd.XPub{}, xpubs[:]...)
+       sort.Sort(signers.SortKeys(cpy))
+       for _, xpub := range cpy {
+               xPubs = append(xPubs, xpub[:]...)
+       }
+       sha3pool.Sum256(hash[:], xPubs)
+       return append(AccountIndexPrefix, hash[:]...)
+}
+
+func Bip44ContractIndexKey(accountID string, change bool) []byte {
+       key := append(ContractIndexPrefix, []byte(accountID)...)
+       if change {
+               return append(key, 0x01)
+       }
+       return append(key, 0x00)
+}
+
+// ContractKey account control promgram store prefix
+func ContractKey(hash bc.Hash) []byte {
+       return append(ContractPrefix, hash.Bytes()...)
+}
+
+// AccountIDKey account id store prefix
+func AccountIDKey(accountID string) []byte {
+       return append(AccountPrefix, []byte(accountID)...)
+}
+
+// StandardUTXOKey makes an account unspent outputs key to store
+func StandardUTXOKey(id bc.Hash) []byte {
+       return append(UTXOPrefix, id.Bytes()...)
+}
+
+// AccountStore satisfies AccountStore interface.
 type AccountStore struct {
-       accountDB dbm.DB
-       batch     dbm.Batch
+       db    dbm.DB
+       batch dbm.Batch
 }
 
 // NewAccountStore create new AccountStore.
 func NewAccountStore(db dbm.DB) *AccountStore {
        return &AccountStore{
-               accountDB: db,
-               batch:     nil,
+               db:    db,
+               batch: nil,
        }
 }
 
-// InitBatch initial batch
-func (store *AccountStore) InitBatch() error {
-       if store.batch != nil {
-               return errors.New("AccountStore initail fail, store batch is not nil.")
-       }
-       store.batch = store.accountDB.NewBatch()
-       return nil
+// InitStore initial new account store
+func (store *AccountStore) InitStore() acc.AccountStore {
+       newStore := NewAccountStore(store.db)
+       newStore.batch = newStore.db.NewBatch()
+       return newStore
 }
 
-// CommitBatch commit batch
-func (store *AccountStore) CommitBatch() error {
+// CommitStore commit batch
+func (store *AccountStore) CommitStore() error {
        if store.batch == nil {
                return errors.New("AccountStore commit fail, store batch is nil.")
        }
@@ -50,24 +101,18 @@ func (store *AccountStore) CommitBatch() error {
 
 // DeleteAccount set account account ID, account alias and raw account.
 func (store *AccountStore) DeleteAccount(account *acc.Account) error {
-       store.DeleteAccountUTXOs(account.ID)
-       batch := store.accountDB.NewBatch()
+       batch := store.db.NewBatch()
        if store.batch != nil {
                batch = store.batch
        }
 
+       // delete account utxos
+       store.deleteAccountUTXOs(account.ID, batch)
+
        // delete account control program
-       cps, err := store.ListControlPrograms()
-       if err != nil {
+       if err := store.deleteAccountControlPrograms(account.ID, batch); err != nil {
                return err
        }
-       var hash [32]byte
-       for _, cp := range cps {
-               if cp.AccountID == account.ID {
-                       sha3pool.Sum256(hash[:], cp.ControlProgram)
-                       batch.Delete(ContractKey(bc.NewHash(hash)))
-               }
-       }
 
        // delete bip44 contract index
        batch.Delete(Bip44ContractIndexKey(account.ID, false))
@@ -85,28 +130,38 @@ func (store *AccountStore) DeleteAccount(account *acc.Account) error {
        return nil
 }
 
-// DeleteAccountUTXOs delete account utxos by accountID
-func (store *AccountStore) DeleteAccountUTXOs(accountID string) error {
-       batch := store.accountDB.NewBatch()
-       if store.batch != nil {
-               batch = store.batch
-       }
-
-       accountUtxoIter := store.accountDB.IteratorPrefix([]byte(UTXOPrefix))
+// deleteAccountUTXOs delete account utxos by accountID
+func (store *AccountStore) deleteAccountUTXOs(accountID string, batch dbm.Batch) error {
+       accountUtxoIter := store.db.IteratorPrefix(UTXOPrefix)
        defer accountUtxoIter.Release()
 
        for accountUtxoIter.Next() {
-               accountUtxo := &acc.UTXO{}
+               accountUtxo := new(acc.UTXO)
                if err := json.Unmarshal(accountUtxoIter.Value(), accountUtxo); err != nil {
                        return err
                }
+
                if accountID == accountUtxo.AccountID {
                        batch.Delete(StandardUTXOKey(accountUtxo.OutputID))
                }
        }
 
-       if store.batch == nil {
-               batch.Write()
+       return nil
+}
+
+// deleteAccountControlPrograms deletes account control program
+func (store *AccountStore) deleteAccountControlPrograms(accountID string, batch dbm.Batch) error {
+       cps, err := store.ListControlPrograms()
+       if err != nil {
+               return err
+       }
+
+       var hash [32]byte
+       for _, cp := range cps {
+               if cp.AccountID == accountID {
+                       sha3pool.Sum256(hash[:], cp.ControlProgram)
+                       batch.Delete(ContractKey(bc.NewHash(hash)))
+               }
        }
        return nil
 }
@@ -114,7 +169,7 @@ func (store *AccountStore) DeleteAccountUTXOs(accountID string) error {
 // DeleteStandardUTXO delete utxo by outpu id
 func (store *AccountStore) DeleteStandardUTXO(outputID bc.Hash) {
        if store.batch == nil {
-               store.accountDB.Delete(StandardUTXOKey(outputID))
+               store.db.Delete(StandardUTXOKey(outputID))
        } else {
                store.batch.Delete(StandardUTXOKey(outputID))
        }
@@ -122,7 +177,7 @@ func (store *AccountStore) DeleteStandardUTXO(outputID bc.Hash) {
 
 // GetAccountByAlias get account by account alias
 func (store *AccountStore) GetAccountByAlias(accountAlias string) (*acc.Account, error) {
-       accountID := store.accountDB.Get(accountAliasKey(accountAlias))
+       accountID := store.db.Get(accountAliasKey(accountAlias))
        if accountID == nil {
                return nil, acc.ErrFindAccount
        }
@@ -131,21 +186,23 @@ func (store *AccountStore) GetAccountByAlias(accountAlias string) (*acc.Account,
 
 // GetAccountByID get account by accountID
 func (store *AccountStore) GetAccountByID(accountID string) (*acc.Account, error) {
-       rawAccount := store.accountDB.Get(AccountIDKey(accountID))
+       rawAccount := store.db.Get(AccountIDKey(accountID))
        if rawAccount == nil {
                return nil, acc.ErrFindAccount
        }
+
        account := new(acc.Account)
        if err := json.Unmarshal(rawAccount, account); err != nil {
                return nil, err
        }
+
        return account, nil
 }
 
 // GetAccountIndex get account index by account xpubs
 func (store *AccountStore) GetAccountIndex(xpubs []chainkd.XPub) uint64 {
        currentIndex := uint64(0)
-       if rawIndexBytes := store.accountDB.Get(accountIndexKey(xpubs)); rawIndexBytes != nil {
+       if rawIndexBytes := store.db.Get(accountIndexKey(xpubs)); rawIndexBytes != nil {
                currentIndex = common.BytesToUnit64(rawIndexBytes)
        }
        return currentIndex
@@ -154,7 +211,7 @@ func (store *AccountStore) GetAccountIndex(xpubs []chainkd.XPub) uint64 {
 // GetBip44ContractIndex get bip44 contract index
 func (store *AccountStore) GetBip44ContractIndex(accountID string, change bool) uint64 {
        index := uint64(0)
-       if rawIndexBytes := store.accountDB.Get(Bip44ContractIndexKey(accountID, change)); rawIndexBytes != nil {
+       if rawIndexBytes := store.db.Get(Bip44ContractIndexKey(accountID, change)); rawIndexBytes != nil {
                index = common.BytesToUnit64(rawIndexBytes)
        }
        return index
@@ -162,13 +219,13 @@ func (store *AccountStore) GetBip44ContractIndex(accountID string, change bool)
 
 // GetCoinbaseArbitrary get coinbase arbitrary
 func (store *AccountStore) GetCoinbaseArbitrary() []byte {
-       return store.accountDB.Get([]byte(CoinbaseAbKey))
+       return store.db.Get(CoinbaseAbKey)
 }
 
 // GetContractIndex get contract index
 func (store *AccountStore) GetContractIndex(accountID string) uint64 {
        index := uint64(0)
-       if rawIndexBytes := store.accountDB.Get(contractIndexKey(accountID)); rawIndexBytes != nil {
+       if rawIndexBytes := store.db.Get(contractIndexKey(accountID)); rawIndexBytes != nil {
                index = common.BytesToUnit64(rawIndexBytes)
        }
        return index
@@ -176,53 +233,60 @@ func (store *AccountStore) GetContractIndex(accountID string) uint64 {
 
 // GetControlProgram get control program
 func (store *AccountStore) GetControlProgram(hash bc.Hash) (*acc.CtrlProgram, error) {
-       rawProgram := store.accountDB.Get(ContractKey(hash))
+       rawProgram := store.db.Get(ContractKey(hash))
        if rawProgram == nil {
                return nil, acc.ErrFindCtrlProgram
        }
+
        cp := new(acc.CtrlProgram)
        if err := json.Unmarshal(rawProgram, cp); err != nil {
                return nil, err
        }
+
        return cp, nil
 }
 
 // GetMiningAddress get mining address
 func (store *AccountStore) GetMiningAddress() (*acc.CtrlProgram, error) {
-       rawCP := store.accountDB.Get([]byte(MiningAddressKey))
+       rawCP := store.db.Get(MiningAddressKey)
        if rawCP == nil {
                return nil, acc.ErrFindMiningAddress
        }
+
        cp := new(acc.CtrlProgram)
        if err := json.Unmarshal(rawCP, cp); err != nil {
                return nil, err
        }
+
        return cp, nil
 }
 
 // GetUTXO get standard utxo by id
 func (store *AccountStore) GetUTXO(outid bc.Hash) (*acc.UTXO, error) {
        u := new(acc.UTXO)
-       if data := store.accountDB.Get(StandardUTXOKey(outid)); data != nil {
+       if data := store.db.Get(StandardUTXOKey(outid)); data != nil {
                return u, json.Unmarshal(data, u)
        }
-       if data := store.accountDB.Get(ContractUTXOKey(outid)); data != nil {
+
+       if data := store.db.Get(ContractUTXOKey(outid)); data != nil {
                return u, json.Unmarshal(data, u)
        }
+
        return nil, acc.ErrMatchUTXO
 }
 
 // ListAccounts get all accounts which name prfix is id.
 func (store *AccountStore) ListAccounts(id string) ([]*acc.Account, error) {
        accounts := []*acc.Account{}
-       accountIter := store.accountDB.IteratorPrefix(AccountIDKey(strings.TrimSpace(id)))
+       accountIter := store.db.IteratorPrefix(AccountIDKey(strings.TrimSpace(id)))
        defer accountIter.Release()
 
        for accountIter.Next() {
                account := new(acc.Account)
-               if err := json.Unmarshal(accountIter.Value(), &account); err != nil {
+               if err := json.Unmarshal(accountIter.Value(), account); err != nil {
                        return nil, err
                }
+
                accounts = append(accounts, account)
        }
        return accounts, nil
@@ -231,36 +295,35 @@ func (store *AccountStore) ListAccounts(id string) ([]*acc.Account, error) {
 // ListControlPrograms get all local control programs
 func (store *AccountStore) ListControlPrograms() ([]*acc.CtrlProgram, error) {
        cps := []*acc.CtrlProgram{}
-       cpIter := store.accountDB.IteratorPrefix([]byte(ContractPrefix))
+       cpIter := store.db.IteratorPrefix(ContractPrefix)
        defer cpIter.Release()
 
        for cpIter.Next() {
                cp := new(acc.CtrlProgram)
-               fmt.Printf("cpiter value: %s, len: %v", cpIter.Value(), len(cpIter.Value()))
-
                if err := json.Unmarshal(cpIter.Value(), cp); err != nil {
                        return nil, err
                }
+
                cps = append(cps, cp)
        }
        return cps, nil
 }
 
 // ListUTXOs get utxos by accountID
-func (store *AccountStore) ListUTXOs() []*acc.UTXO {
-       utxoIter := store.accountDB.IteratorPrefix([]byte(UTXOPrefix))
+func (store *AccountStore) ListUTXOs() ([]*acc.UTXO, error) {
+       utxoIter := store.db.IteratorPrefix(UTXOPrefix)
        defer utxoIter.Release()
 
        utxos := []*acc.UTXO{}
        for utxoIter.Next() {
                utxo := new(acc.UTXO)
                if err := json.Unmarshal(utxoIter.Value(), utxo); err != nil {
-                       log.WithFields(log.Fields{"module": logModule, "err": err}).Error("utxoKeeper findUtxos fail on unmarshal utxo")
-                       continue
+                       return nil, err
                }
+
                utxos = append(utxos, utxo)
        }
-       return utxos
+       return utxos, nil
 }
 
 // SetAccount set account account ID, account alias and raw account.
@@ -270,7 +333,7 @@ func (store *AccountStore) SetAccount(account *acc.Account) error {
                return acc.ErrMarshalAccount
        }
 
-       batch := store.accountDB.NewBatch()
+       batch := store.db.NewBatch()
        if store.batch != nil {
                batch = store.batch
        }
@@ -284,32 +347,22 @@ func (store *AccountStore) SetAccount(account *acc.Account) error {
        return nil
 }
 
-// SetAccountIndex set account account ID, account alias and raw account.
-func (store *AccountStore) SetAccountIndex(account *acc.Account) error {
-       rawAccount, err := json.Marshal(account)
-       if err != nil {
-               return acc.ErrMarshalAccount
-       }
-
-       batch := store.accountDB.NewBatch()
-       if store.batch != nil {
-               batch = store.batch
-       }
-
-       batch.Set(AccountIDKey(account.ID), rawAccount)
-       batch.Set(accountAliasKey(account.Alias), []byte(account.ID))
-       batch.Set(accountIndexKey(account.XPubs), common.Unit64ToBytes(account.KeyIndex))
-
-       if store.batch == nil {
-               batch.Write()
+// SetAccountIndex update account index
+func (store *AccountStore) SetAccountIndex(account *acc.Account) {
+       currentIndex := store.GetAccountIndex(account.XPubs)
+       if account.KeyIndex > currentIndex {
+               if store.batch == nil {
+                       store.db.Set(accountIndexKey(account.XPubs), common.Unit64ToBytes(account.KeyIndex))
+               } else {
+                       store.batch.Set(accountIndexKey(account.XPubs), common.Unit64ToBytes(account.KeyIndex))
+               }
        }
-       return nil
 }
 
 // SetBip44ContractIndex set contract index
 func (store *AccountStore) SetBip44ContractIndex(accountID string, change bool, index uint64) {
        if store.batch == nil {
-               store.accountDB.Set(Bip44ContractIndexKey(accountID, change), common.Unit64ToBytes(index))
+               store.db.Set(Bip44ContractIndexKey(accountID, change), common.Unit64ToBytes(index))
        } else {
                store.batch.Set(Bip44ContractIndexKey(accountID, change), common.Unit64ToBytes(index))
        }
@@ -318,16 +371,16 @@ func (store *AccountStore) SetBip44ContractIndex(accountID string, change bool,
 // SetCoinbaseArbitrary set coinbase arbitrary
 func (store *AccountStore) SetCoinbaseArbitrary(arbitrary []byte) {
        if store.batch == nil {
-               store.accountDB.Set([]byte(CoinbaseAbKey), arbitrary)
+               store.db.Set(CoinbaseAbKey, arbitrary)
        } else {
-               store.batch.Set([]byte(CoinbaseAbKey), arbitrary)
+               store.batch.Set(CoinbaseAbKey, arbitrary)
        }
 }
 
 // SetContractIndex set contract index
 func (store *AccountStore) SetContractIndex(accountID string, index uint64) {
        if store.batch == nil {
-               store.accountDB.Set(contractIndexKey(accountID), common.Unit64ToBytes(index))
+               store.db.Set(contractIndexKey(accountID), common.Unit64ToBytes(index))
        } else {
                store.batch.Set(contractIndexKey(accountID), common.Unit64ToBytes(index))
        }
@@ -340,7 +393,7 @@ func (store *AccountStore) SetControlProgram(hash bc.Hash, program *acc.CtrlProg
                return err
        }
        if store.batch == nil {
-               store.accountDB.Set(ContractKey(hash), accountCP)
+               store.db.Set(ContractKey(hash), accountCP)
        } else {
                store.batch.Set(ContractKey(hash), accountCP)
        }
@@ -355,9 +408,9 @@ func (store *AccountStore) SetMiningAddress(program *acc.CtrlProgram) error {
        }
 
        if store.batch == nil {
-               store.accountDB.Set([]byte(MiningAddressKey), rawProgram)
+               store.db.Set(MiningAddressKey, rawProgram)
        } else {
-               store.batch.Set([]byte(MiningAddressKey), rawProgram)
+               store.batch.Set(MiningAddressKey, rawProgram)
        }
        return nil
 }
@@ -368,8 +421,9 @@ func (store *AccountStore) SetStandardUTXO(outputID bc.Hash, utxo *acc.UTXO) err
        if err != nil {
                return err
        }
+
        if store.batch == nil {
-               store.accountDB.Set(StandardUTXOKey(outputID), data)
+               store.db.Set(StandardUTXOKey(outputID), data)
        } else {
                store.batch.Set(StandardUTXOKey(outputID), data)
        }