OSDN Git Service

update from master
authorChengcheng Zhang <943420582@qq.com>
Tue, 2 Jul 2019 13:37:58 +0000 (21:37 +0800)
committerChengcheng Zhang <943420582@qq.com>
Tue, 2 Jul 2019 13:37:58 +0000 (21:37 +0800)
1  2 
database/wallet_store.go
wallet/indexer.go
wallet/unconfirmed.go

diff --combined database/wallet_store.go
index 158bf7e,0000000..eb4ee67
mode 100644,000000..100644
--- /dev/null
@@@ -1,548 -1,0 +1,548 @@@
-       itr := store.walletDB.IteratorPrefixWithStart([]byte(preFix), startKey)
 +package database
 +
 +import (
 +      "encoding/binary"
 +      "encoding/json"
 +      "fmt"
 +      "sort"
 +
 +      acc "github.com/vapor/account"
 +      "github.com/vapor/asset"
 +      "github.com/vapor/blockchain/query"
 +      "github.com/vapor/blockchain/signers"
 +      "github.com/vapor/crypto/ed25519/chainkd"
 +      "github.com/vapor/crypto/sha3pool"
 +      dbm "github.com/vapor/database/leveldb"
 +      "github.com/vapor/errors"
 +      "github.com/vapor/protocol/bc"
 +)
 +
 +const (
 +      utxoPrefix  byte = iota //UTXOPrefix is StandardUTXOKey prefix
 +      sutxoPrefix             //SUTXOPrefix is ContractUTXOKey prefix
 +      contractPrefix
 +      contractIndexPrefix
 +      accountPrefix // AccountPrefix is account ID prefix
 +      accountAliasPrefix
 +      accountIndexPrefix
 +      txPrefix            //TxPrefix is wallet database transactions prefix
 +      txIndexPrefix       //TxIndexPrefix is wallet database tx index prefix
 +      unconfirmedTxPrefix //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
 +      globalTxIndexPrefix //GlobalTxIndexPrefix is wallet database global tx index prefix
 +      walletKey
 +      miningAddressKey
 +      coinbaseAbKey
 +      recoveryKey
 +)
 +
 +// leveldb key prefix
 +var (
 +      UTXOPrefix  = []byte{utxoPrefix, colon}
 +      SUTXOPrefix = []byte{sutxoPrefix, colon}
 +      // ContractPrefix = []byte{contractPrefix, contractPrefix, colon}
 +      ContractPrefix      = "Contract:"
 +      ContractIndexPrefix = []byte{contractIndexPrefix, colon}
 +      AccountPrefix       = []byte{accountPrefix, colon} // AccountPrefix is account ID prefix
 +      AccountAliasPrefix  = []byte{accountAliasPrefix, colon}
 +      AccountIndexPrefix  = []byte{accountIndexPrefix, colon}
 +      TxPrefix            = []byte{txPrefix, colon}            //TxPrefix is wallet database transactions prefix
 +      TxIndexPrefix       = []byte{txIndexPrefix, colon}       //TxIndexPrefix is wallet database tx index prefix
 +      UnconfirmedTxPrefix = []byte{unconfirmedTxPrefix, colon} //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
 +      GlobalTxIndexPrefix = []byte{globalTxIndexPrefix, colon} //GlobalTxIndexPrefix is wallet database global tx index prefix
 +      WalletKey           = []byte{walletKey}
 +      MiningAddressKey    = []byte{miningAddressKey}
 +      CoinbaseAbKey       = []byte{coinbaseAbKey}
 +      RecoveryKey         = []byte{recoveryKey}
 +)
 +
 +// errors
 +var (
 +      // ErrFindAccount        = errors.New("Failed to find account")
 +      errAccntTxIDNotFound = errors.New("account TXID not found")
 +      errGetAsset          = errors.New("Failed to find asset definition")
 +)
 +
 +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([]byte(AccountIndexPrefix), hash[:]...)
 +}
 +
 +func Bip44ContractIndexKey(accountID string, change bool) []byte {
 +      key := append([]byte(ContractIndexPrefix), accountID...)
 +      if change {
 +              return append(key, []byte{1}...)
 +      }
 +      return append(key, []byte{0}...)
 +}
 +
 +// ContractKey account control promgram store prefix
 +func ContractKey(hash bc.Hash) []byte {
 +      // h := hash.String()
 +      // return append([]byte(ContractPrefix), []byte(h)...)
 +      return append([]byte(ContractPrefix), hash.Bytes()...)
 +}
 +
 +// AccountIDKey account id store prefix
 +func AccountIDKey(accountID string) []byte {
 +      return append([]byte(AccountPrefix), []byte(accountID)...)
 +}
 +
 +// StandardUTXOKey makes an account unspent outputs key to store
 +func StandardUTXOKey(id bc.Hash) []byte {
 +      name := id.String()
 +      return append(UTXOPrefix, []byte(name)...)
 +}
 +
 +// ContractUTXOKey makes a smart contract unspent outputs key to store
 +func ContractUTXOKey(id bc.Hash) []byte {
 +      name := id.String()
 +      return append(SUTXOPrefix, []byte(name)...)
 +}
 +
 +func calcDeleteKey(blockHeight uint64) []byte {
 +      return []byte(fmt.Sprintf("%s%016x", TxPrefix, blockHeight))
 +}
 +
 +func calcTxIndexKey(txID string) []byte {
 +      return append(TxIndexPrefix, []byte(txID)...)
 +}
 +
 +func calcAnnotatedKey(formatKey string) []byte {
 +      return append(TxPrefix, []byte(formatKey)...)
 +}
 +
 +func calcUnconfirmedTxKey(formatKey string) []byte {
 +      return append(UnconfirmedTxPrefix, []byte(formatKey)...)
 +}
 +
 +func calcGlobalTxIndexKey(txID string) []byte {
 +      return append(GlobalTxIndexPrefix, []byte(txID)...)
 +}
 +
 +func CalcGlobalTxIndex(blockHash *bc.Hash, position uint64) []byte {
 +      txIdx := make([]byte, 40)
 +      copy(txIdx[:32], blockHash.Bytes())
 +      binary.BigEndian.PutUint64(txIdx[32:], position)
 +      return txIdx
 +}
 +
 +func formatKey(blockHeight uint64, position uint32) string {
 +      return fmt.Sprintf("%016x%08x", blockHeight, position)
 +}
 +
 +func contractIndexKey(accountID string) []byte {
 +      return append([]byte(ContractIndexPrefix), []byte(accountID)...)
 +}
 +
 +func accountAliasKey(name string) []byte {
 +      return append([]byte(AccountAliasPrefix), []byte(name)...)
 +}
 +
 +// WalletStore store wallet using leveldb
 +type WalletStore struct {
 +      walletDB dbm.DB
 +      batch    dbm.Batch
 +}
 +
 +// NewWalletStore create new WalletStore struct
 +func NewWalletStore(db dbm.DB) *WalletStore {
 +      return &WalletStore{
 +              walletDB: db,
 +              batch:    nil,
 +      }
 +}
 +
 +// InitBatch initial batch
 +func (store *WalletStore) InitBatch() error {
 +      if store.batch != nil {
 +              return errors.New("WalletStore initail fail, store batch is not nil.")
 +      }
 +      store.batch = store.walletDB.NewBatch()
 +      return nil
 +}
 +
 +// CommitBatch commit batch
 +func (store *WalletStore) CommitBatch() error {
 +      if store.batch == nil {
 +              return errors.New("WalletStore commit fail, store batch is nil.")
 +      }
 +      store.batch.Write()
 +      store.batch = nil
 +      return nil
 +}
 +
 +// DeleteContractUTXO delete contract utxo by outputID
 +func (store *WalletStore) DeleteContractUTXO(outputID bc.Hash) {
 +      if store.batch == nil {
 +              store.walletDB.Delete(ContractUTXOKey(outputID))
 +      } else {
 +              store.batch.Delete(ContractUTXOKey(outputID))
 +      }
 +}
 +
 +// DeleteRecoveryStatus delete recovery status
 +func (store *WalletStore) DeleteRecoveryStatus() {
 +      if store.batch == nil {
 +              store.walletDB.Delete(RecoveryKey)
 +      } else {
 +              store.batch.Delete(RecoveryKey)
 +      }
 +}
 +
 +// DeleteStardardUTXO delete stardard utxo by outputID
 +func (store *WalletStore) DeleteStardardUTXO(outputID bc.Hash) {
 +      if store.batch == nil {
 +              store.walletDB.Delete(StandardUTXOKey(outputID))
 +      } else {
 +              store.batch.Delete(StandardUTXOKey(outputID))
 +      }
 +}
 +
 +// DeleteTransactions delete transactions when orphan block rollback
 +func (store *WalletStore) DeleteTransactions(height uint64) {
 +      batch := store.walletDB.NewBatch()
 +      if store.batch != nil {
 +              batch = store.batch
 +      }
 +      txIter := store.walletDB.IteratorPrefix(calcDeleteKey(height))
 +      defer txIter.Release()
 +
 +      tmpTx := query.AnnotatedTx{}
 +      for txIter.Next() {
 +              if err := json.Unmarshal(txIter.Value(), &tmpTx); err == nil {
 +                      batch.Delete(calcTxIndexKey(tmpTx.ID.String()))
 +              }
 +              batch.Delete(txIter.Key())
 +      }
 +      if store.batch == nil {
 +              batch.Write()
 +      }
 +}
 +
 +// DeleteUnconfirmedTransaction delete unconfirmed tx by txID
 +func (store *WalletStore) DeleteUnconfirmedTransaction(txID string) {
 +      if store.batch == nil {
 +              store.walletDB.Delete(calcUnconfirmedTxKey(txID))
 +      } else {
 +              store.batch.Delete(calcUnconfirmedTxKey(txID))
 +      }
 +}
 +
 +// DeleteWalletTransactions delete all txs in wallet
 +func (store *WalletStore) DeleteWalletTransactions() {
 +      batch := store.walletDB.NewBatch()
 +      if store.batch != nil {
 +              batch = store.batch
 +      }
 +      txIter := store.walletDB.IteratorPrefix([]byte(TxPrefix))
 +      defer txIter.Release()
 +
 +      for txIter.Next() {
 +              batch.Delete(txIter.Key())
 +      }
 +
 +      txIndexIter := store.walletDB.IteratorPrefix([]byte(TxIndexPrefix))
 +      defer txIndexIter.Release()
 +
 +      for txIndexIter.Next() {
 +              batch.Delete(txIndexIter.Key())
 +      }
 +      if store.batch == nil {
 +              batch.Write()
 +      }
 +}
 +
 +// DeleteWalletUTXOs delete all txs in wallet
 +func (store *WalletStore) DeleteWalletUTXOs() {
 +      batch := store.walletDB.NewBatch()
 +      if store.batch != nil {
 +              batch = store.batch
 +      }
 +      ruIter := store.walletDB.IteratorPrefix([]byte(UTXOPrefix))
 +      defer ruIter.Release()
 +      for ruIter.Next() {
 +              batch.Delete(ruIter.Key())
 +      }
 +
 +      suIter := store.walletDB.IteratorPrefix([]byte(SUTXOPrefix))
 +      defer suIter.Release()
 +      for suIter.Next() {
 +              batch.Delete(suIter.Key())
 +      }
 +      if store.batch == nil {
 +              batch.Write()
 +      }
 +}
 +
 +// GetAccount get account value by account ID
 +func (store *WalletStore) GetAccount(accountID string) (*acc.Account, error) {
 +      rawAccount := store.walletDB.Get(AccountIDKey(accountID))
 +      if rawAccount == nil {
 +              return nil, fmt.Errorf("failed get account, accountID: %s ", accountID)
 +      }
 +      account := new(acc.Account)
 +      if err := json.Unmarshal(rawAccount, account); err != nil {
 +              return nil, err
 +      }
 +      return account, nil
 +}
 +
 +// GetAsset get asset by assetID
 +func (store *WalletStore) GetAsset(assetID *bc.AssetID) (*asset.Asset, error) {
 +      definitionByte := store.walletDB.Get(asset.ExtAssetKey(assetID))
 +      if definitionByte == nil {
 +              return nil, errGetAsset
 +      }
 +      definitionMap := make(map[string]interface{})
 +      if err := json.Unmarshal(definitionByte, &definitionMap); err != nil {
 +              return nil, err
 +      }
 +      alias := assetID.String()
 +      externalAsset := &asset.Asset{
 +              AssetID:           *assetID,
 +              Alias:             &alias,
 +              DefinitionMap:     definitionMap,
 +              RawDefinitionByte: definitionByte,
 +      }
 +      return externalAsset, nil
 +}
 +
 +// GetControlProgram get raw program by hash
 +func (store *WalletStore) GetControlProgram(hash bc.Hash) (*acc.CtrlProgram, error) {
 +      rawProgram := store.walletDB.Get(ContractKey(hash))
 +      if rawProgram == nil {
 +              return nil, fmt.Errorf("failed get account control program:%x ", hash)
 +      }
 +      accountCP := new(acc.CtrlProgram)
 +      if err := json.Unmarshal(rawProgram, &accountCP); err != nil {
 +              return nil, err
 +      }
 +      return accountCP, nil
 +}
 +
 +// GetGlobalTransactionIndex get global tx by txID
 +func (store *WalletStore) GetGlobalTransactionIndex(txID string) []byte {
 +      return store.walletDB.Get(calcGlobalTxIndexKey(txID))
 +}
 +
 +// GetStandardUTXO get standard utxo by id
 +func (store *WalletStore) GetStandardUTXO(outid bc.Hash) (*acc.UTXO, error) {
 +      rawUTXO := store.walletDB.Get(StandardUTXOKey(outid))
 +      if rawUTXO == nil {
 +              return nil, fmt.Errorf("failed get standard UTXO, outputID: %s ", outid.String())
 +      }
 +      UTXO := new(acc.UTXO)
 +      if err := json.Unmarshal(rawUTXO, UTXO); err != nil {
 +              return nil, err
 +      }
 +      return UTXO, nil
 +}
 +
 +// GetTransaction get tx by txid
 +func (store *WalletStore) GetTransaction(txID string) (*query.AnnotatedTx, error) {
 +      formatKey := store.walletDB.Get(calcTxIndexKey(txID))
 +      if formatKey == nil {
 +              return nil, errAccntTxIDNotFound
 +      }
 +      rawTx := store.walletDB.Get(calcAnnotatedKey(string(formatKey)))
 +      tx := new(query.AnnotatedTx)
 +      if err := json.Unmarshal(rawTx, tx); err != nil {
 +              return nil, err
 +      }
 +      return tx, nil
 +}
 +
 +// GetUnconfirmedTransaction get unconfirmed tx by txID
 +func (store *WalletStore) GetUnconfirmedTransaction(txID string) (*query.AnnotatedTx, error) {
 +      rawUnconfirmedTx := store.walletDB.Get(calcUnconfirmedTxKey(txID))
 +      if rawUnconfirmedTx == nil {
 +              return nil, fmt.Errorf("failed get unconfirmed tx, txID: %s ", txID)
 +      }
 +      tx := new(query.AnnotatedTx)
 +      if err := json.Unmarshal(rawUnconfirmedTx, tx); err != nil {
 +              return nil, err
 +      }
 +      return tx, nil
 +}
 +
 +// GetRecoveryStatus delete recovery status
 +func (store *WalletStore) GetRecoveryStatus(recoveryKey []byte) []byte {
 +      return store.walletDB.Get(recoveryKey)
 +}
 +
 +// GetWalletInfo get wallet information
 +func (store *WalletStore) GetWalletInfo() []byte {
 +      return store.walletDB.Get([]byte(WalletKey))
 +}
 +
 +// ListAccountUTXOs get all account unspent outputs
 +func (store *WalletStore) ListAccountUTXOs(key string) ([]*acc.UTXO, error) {
 +      accountUtxoIter := store.walletDB.IteratorPrefix([]byte(key))
 +      defer accountUtxoIter.Release()
 +
 +      confirmedUTXOs := []*acc.UTXO{}
 +      for accountUtxoIter.Next() {
 +              utxo := new(acc.UTXO)
 +              if err := json.Unmarshal(accountUtxoIter.Value(), utxo); err != nil {
 +                      return nil, err
 +              }
 +              confirmedUTXOs = append(confirmedUTXOs, utxo)
 +      }
 +      return confirmedUTXOs, nil
 +}
 +
 +func (store *WalletStore) ListTransactions(accountID string, StartTxID string, count uint, unconfirmed bool) ([]*query.AnnotatedTx, error) {
 +      annotatedTxs := []*query.AnnotatedTx{}
 +      var startKey []byte
 +      preFix := TxPrefix
 +
 +      if StartTxID != "" {
 +              if unconfirmed {
 +                      startKey = calcUnconfirmedTxKey(StartTxID)
 +              } else {
 +                      formatKey := store.walletDB.Get(calcTxIndexKey(StartTxID))
 +                      if formatKey == nil {
 +                              return nil, errAccntTxIDNotFound
 +                      }
 +                      startKey = calcAnnotatedKey(string(formatKey))
 +              }
 +      }
 +
 +      if unconfirmed {
 +              preFix = UnconfirmedTxPrefix
 +      }
 +
++      itr := store.walletDB.IteratorPrefixWithStart([]byte(preFix), startKey, true)
 +      defer itr.Release()
 +
 +      for txNum := count; itr.Next() && txNum > 0; txNum-- {
 +              annotatedTx := new(query.AnnotatedTx)
 +              if err := json.Unmarshal(itr.Value(), &annotatedTx); err != nil {
 +                      return nil, err
 +              }
 +              annotatedTxs = append(annotatedTxs, annotatedTx)
 +      }
 +
 +      return annotatedTxs, nil
 +}
 +
 +// ListUnconfirmedTransactions get all unconfirmed txs
 +func (store *WalletStore) ListUnconfirmedTransactions() ([]*query.AnnotatedTx, error) {
 +      annotatedTxs := []*query.AnnotatedTx{}
 +      txIter := store.walletDB.IteratorPrefix([]byte(UnconfirmedTxPrefix))
 +      defer txIter.Release()
 +
 +      for txIter.Next() {
 +              annotatedTx := &query.AnnotatedTx{}
 +              if err := json.Unmarshal(txIter.Value(), &annotatedTx); err != nil {
 +                      return nil, err
 +              }
 +              annotatedTxs = append(annotatedTxs, annotatedTx)
 +      }
 +      return annotatedTxs, nil
 +}
 +
 +// SetAssetDefinition set assetID and definition
 +func (store *WalletStore) SetAssetDefinition(assetID *bc.AssetID, definition []byte) {
 +      if store.batch == nil {
 +              store.walletDB.Set(asset.ExtAssetKey(assetID), definition)
 +      } else {
 +              store.batch.Set(asset.ExtAssetKey(assetID), definition)
 +      }
 +}
 +
 +// SetContractUTXO set standard utxo
 +func (store *WalletStore) SetContractUTXO(outputID bc.Hash, utxo *acc.UTXO) error {
 +      data, err := json.Marshal(utxo)
 +      if err != nil {
 +              return err
 +      }
 +      if store.batch == nil {
 +              store.walletDB.Set(ContractUTXOKey(outputID), data)
 +      } else {
 +              store.batch.Set(ContractUTXOKey(outputID), data)
 +      }
 +      return nil
 +}
 +
 +// SetGlobalTransactionIndex set global tx index by blockhash and position
 +func (store *WalletStore) SetGlobalTransactionIndex(globalTxID string, blockHash *bc.Hash, position uint64) {
 +      if store.batch == nil {
 +              store.walletDB.Set(calcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
 +      } else {
 +              store.batch.Set(calcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
 +      }
 +}
 +
 +// SetRecoveryStatus set recovery status
 +func (store *WalletStore) SetRecoveryStatus(recoveryKey, rawStatus []byte) {
 +      if store.batch == nil {
 +              store.walletDB.Set(recoveryKey, rawStatus)
 +      } else {
 +              store.batch.Set(recoveryKey, rawStatus)
 +      }
 +}
 +
 +// SetStandardUTXO set standard utxo
 +func (store *WalletStore) SetStandardUTXO(outputID bc.Hash, utxo *acc.UTXO) error {
 +      data, err := json.Marshal(utxo)
 +      if err != nil {
 +              return err
 +      }
 +      if store.batch == nil {
 +              store.walletDB.Set(StandardUTXOKey(outputID), data)
 +      } else {
 +              store.batch.Set(StandardUTXOKey(outputID), data)
 +      }
 +      return nil
 +}
 +
 +// SetTransaction set raw transaction by block height and tx position
 +func (store *WalletStore) SetTransaction(height uint64, tx *query.AnnotatedTx) error {
 +      batch := store.walletDB.NewBatch()
 +      if store.batch != nil {
 +              batch = store.batch
 +      }
 +
 +      rawTx, err := json.Marshal(tx)
 +      if err != nil {
 +              return err
 +      }
 +      batch.Set(calcAnnotatedKey(formatKey(height, tx.Position)), rawTx)
 +      batch.Set(calcTxIndexKey(tx.ID.String()), []byte(formatKey(height, tx.Position)))
 +
 +      if store.batch == nil {
 +              batch.Write()
 +      }
 +      return nil
 +}
 +
 +// SetUnconfirmedTransaction set unconfirmed tx by txID
 +func (store *WalletStore) SetUnconfirmedTransaction(txID string, tx *query.AnnotatedTx) error {
 +      rawTx, err := json.Marshal(tx)
 +      if err != nil {
 +              return err
 +      }
 +      if store.batch == nil {
 +              store.walletDB.Set(calcUnconfirmedTxKey(txID), rawTx)
 +      } else {
 +              store.batch.Set(calcUnconfirmedTxKey(txID), rawTx)
 +      }
 +      return nil
 +}
 +
 +// SetWalletInfo get wallet information
 +func (store *WalletStore) SetWalletInfo(rawWallet []byte) {
 +      if store.batch == nil {
 +              store.walletDB.Set([]byte(WalletKey), rawWallet)
 +      } else {
 +              store.batch.Set([]byte(WalletKey), rawWallet)
 +      }
 +}
diff --combined wallet/indexer.go
@@@ -3,20 -3,62 +3,20 @@@ package walle
  import (
        "encoding/binary"
        "encoding/hex"
 -      "encoding/json"
        "fmt"
        "sort"
  
        log "github.com/sirupsen/logrus"
  
        "github.com/vapor/account"
 -      "github.com/vapor/asset"
        "github.com/vapor/blockchain/query"
        "github.com/vapor/consensus"
        "github.com/vapor/crypto/sha3pool"
 -      dbm "github.com/vapor/database/leveldb"
        chainjson "github.com/vapor/encoding/json"
 -      "github.com/vapor/errors"
        "github.com/vapor/protocol/bc"
        "github.com/vapor/protocol/bc/types"
  )
  
 -const (
 -      //TxPrefix is wallet database transactions prefix
 -      TxPrefix = "TXS:"
 -      //TxIndexPrefix is wallet database tx index prefix
 -      TxIndexPrefix = "TID:"
 -      //TxIndexPrefix is wallet database global tx index prefix
 -      GlobalTxIndexPrefix = "GTID:"
 -)
 -
 -var ErrAccntTxIDNotFound = errors.New("account TXID not found")
 -
 -func formatKey(blockHeight uint64, position uint32) string {
 -      return fmt.Sprintf("%016x%08x", blockHeight, position)
 -}
 -
 -func calcAnnotatedKey(formatKey string) []byte {
 -      return []byte(TxPrefix + formatKey)
 -}
 -
 -func calcDeleteKey(blockHeight uint64) []byte {
 -      return []byte(fmt.Sprintf("%s%016x", TxPrefix, blockHeight))
 -}
 -
 -func calcTxIndexKey(txID string) []byte {
 -      return []byte(TxIndexPrefix + txID)
 -}
 -
 -func calcGlobalTxIndexKey(txID string) []byte {
 -      return []byte(GlobalTxIndexPrefix + txID)
 -}
 -
 -func calcGlobalTxIndex(blockHash *bc.Hash, position uint64) []byte {
 -      txIdx := make([]byte, 40)
 -      copy(txIdx[:32], blockHash.Bytes())
 -      binary.BigEndian.PutUint64(txIdx[32:], position)
 -      return txIdx
 -}
 -
  func parseGlobalTxIdx(globalTxIdx []byte) (*bc.Hash, uint64) {
        var hashBytes [32]byte
        copy(hashBytes[:], globalTxIdx[:32])
        return &hash, position
  }
  
 -// deleteTransaction delete transactions when orphan block rollback
 -func (w *Wallet) deleteTransactions(batch dbm.Batch, height uint64) {
 -      tmpTx := query.AnnotatedTx{}
 -      txIter := w.DB.IteratorPrefix(calcDeleteKey(height))
 -      defer txIter.Release()
 -
 -      for txIter.Next() {
 -              if err := json.Unmarshal(txIter.Value(), &tmpTx); err == nil {
 -                      batch.Delete(calcTxIndexKey(tmpTx.ID.String()))
 -              }
 -              batch.Delete(txIter.Key())
 -      }
 -}
 -
  // saveExternalAssetDefinition save external and local assets definition,
  // when query ,query local first and if have no then query external
  // details see getAliasDefinition
 -func saveExternalAssetDefinition(b *types.Block, walletDB dbm.DB) {
 -      storeBatch := walletDB.NewBatch()
 -      defer storeBatch.Write()
 +func saveExternalAssetDefinition(b *types.Block, store WalletStorer) error {
 +      if err := store.InitBatch(); err != nil {
 +              return err
 +      }
  
        for _, tx := range b.Transactions {
                for _, orig := range tx.Inputs {
                        if cci, ok := orig.TypedInput.(*types.CrossChainInput); ok {
                                assetID := cci.AssetId
 -                              if assetExist := walletDB.Get(asset.ExtAssetKey(assetID)); assetExist == nil {
 -                                      storeBatch.Set(asset.ExtAssetKey(assetID), cci.AssetDefinition)
 +                              assetExist, err := store.GetAsset(assetID)
 +                              if err != nil {
 +                                      return err
 +                              }
 +                              if assetExist == nil {
 +                                      store.SetAssetDefinition(assetID, cci.AssetDefinition)
                                }
                        }
                }
        }
 +
 +      if err := store.CommitBatch(); err != nil {
 +              return err
 +      }
 +      return nil
  }
  
  // Summary is the struct of transaction's input and output summary
@@@ -74,12 -120,23 +74,12 @@@ type TxSummary struct 
  }
  
  // indexTransactions saves all annotated transactions to the database.
 -func (w *Wallet) indexTransactions(batch dbm.Batch, b *types.Block, txStatus *bc.TransactionStatus) error {
 -      annotatedTxs := w.filterAccountTxs(b, txStatus)
 -      saveExternalAssetDefinition(b, w.DB)
 -      annotateTxsAccount(annotatedTxs, w.DB)
 -
 +func (w *Wallet) indexTransactions(b *types.Block, txStatus *bc.TransactionStatus, annotatedTxs []*query.AnnotatedTx) error {
        for _, tx := range annotatedTxs {
 -              rawTx, err := json.Marshal(tx)
 -              if err != nil {
 -                      log.WithFields(log.Fields{"module": logModule, "err": err}).Error("inserting annotated_txs to db")
 +              if err := w.store.SetTransaction(b.Height, tx); err != nil {
                        return err
                }
 -
 -              batch.Set(calcAnnotatedKey(formatKey(b.Height, uint32(tx.Position))), rawTx)
 -              batch.Set(calcTxIndexKey(tx.ID.String()), []byte(formatKey(b.Height, uint32(tx.Position))))
 -
 -              // delete unconfirmed transaction
 -              batch.Delete(calcUnconfirmedTxKey(tx.ID.String()))
 +              w.store.DeleteUnconfirmedTransaction(tx.ID.String())
        }
  
        if !w.TxIndexFlag {
  
        for position, globalTx := range b.Transactions {
                blockHash := b.BlockHeader.Hash()
 -              batch.Set(calcGlobalTxIndexKey(globalTx.ID.String()), calcGlobalTxIndex(&blockHash, uint64(position)))
 +              w.store.SetGlobalTransactionIndex(globalTx.ID.String(), &blockHash, uint64(position))
        }
  
        return nil
@@@ -105,28 -162,21 +105,28 @@@ transactionLoop
                        var hash [32]byte
                        sha3pool.Sum256(hash[:], v.ControlProgram())
  
 -                      if bytes := w.DB.Get(account.ContractKey(hash)); bytes != nil {
 -                              annotatedTxs = append(annotatedTxs, w.buildAnnotatedTransaction(tx, b, statusFail, pos))
 -                              continue transactionLoop
 +                      _, err := w.store.GetControlProgram(bc.NewHash(hash))
 +                      if err != nil {
 +                              log.WithFields(log.Fields{"module": logModule, "err": err, "hash": string(hash[:])}).Error("filterAccountTxs fail.")
 +                              continue
                        }
 +                      annotatedTxs = append(annotatedTxs, w.buildAnnotatedTransaction(tx, b, statusFail, pos))
 +                      continue transactionLoop
                }
  
                for _, v := range tx.Inputs {
                        outid, err := v.SpentOutputID()
                        if err != nil {
 +                              log.WithFields(log.Fields{"module": logModule, "err": err, "outputID": outid.String()}).Error("filterAccountTxs fail.")
                                continue
                        }
 -                      if bytes := w.DB.Get(account.StandardUTXOKey(outid)); bytes != nil {
 -                              annotatedTxs = append(annotatedTxs, w.buildAnnotatedTransaction(tx, b, statusFail, pos))
 -                              continue transactionLoop
 +                      _, err = w.store.GetStandardUTXO(outid)
 +                      if err != nil {
 +                              log.WithFields(log.Fields{"module": logModule, "err": err, "outputID": outid.String()}).Error("filterAccountTxs fail.")
 +                              continue
                        }
 +                      annotatedTxs = append(annotatedTxs, w.buildAnnotatedTransaction(tx, b, statusFail, pos))
 +                      continue transactionLoop
                }
        }
  
@@@ -145,16 -195,23 +145,16 @@@ func (w *Wallet) GetTransactionByTxID(t
  }
  
  func (w *Wallet) getAccountTxByTxID(txID string) (*query.AnnotatedTx, error) {
 -      annotatedTx := &query.AnnotatedTx{}
 -      formatKey := w.DB.Get(calcTxIndexKey(txID))
 -      if formatKey == nil {
 -              return nil, ErrAccntTxIDNotFound
 -      }
 -
 -      txInfo := w.DB.Get(calcAnnotatedKey(string(formatKey)))
 -      if err := json.Unmarshal(txInfo, annotatedTx); err != nil {
 +      annotatedTx, err := w.store.GetTransaction(txID)
 +      if err != nil {
                return nil, err
        }
 -
        annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
        return annotatedTx, nil
  }
  
  func (w *Wallet) getGlobalTxByTxID(txID string) (*query.AnnotatedTx, error) {
 -      globalTxIdx := w.DB.Get(calcGlobalTxIndexKey(txID))
 +      globalTxIdx := w.store.GetGlobalTransactionIndex(txID)
        if globalTxIdx == nil {
                return nil, fmt.Errorf("No transaction(tx_id=%s) ", txID)
        }
@@@ -234,24 -291,47 +234,26 @@@ func findTransactionsByAccount(annotate
  // GetTransactions get all walletDB transactions or unconfirmed transactions, and filter transactions by accountID and StartTxID optional
  func (w *Wallet) GetTransactions(accountID string, StartTxID string, count uint, unconfirmed bool) ([]*query.AnnotatedTx, error) {
        annotatedTxs := []*query.AnnotatedTx{}
 -      var startKey []byte
 -      preFix := TxPrefix
 -
 -      if StartTxID != "" {
 -              if unconfirmed {
 -                      startKey = calcUnconfirmedTxKey(StartTxID)
 -              } else {
 -                      formatKey := w.DB.Get(calcTxIndexKey(StartTxID))
 -                      if formatKey == nil {
 -                              return nil, ErrAccntTxIDNotFound
 -                      }
 -                      startKey = calcAnnotatedKey(string(formatKey))
 -              }
 -      }
 -
 -      if unconfirmed {
 -              preFix = UnconfirmedTxPrefix
 +      annotatedTxs, err := w.store.ListTransactions(accountID, StartTxID, count, unconfirmed)
 +      if err != nil {
 +              return nil, err
        }
  
 -      itr := w.DB.IteratorPrefixWithStart([]byte(preFix), startKey, true)
 -      defer itr.Release()
 -
 -      for txNum := count; itr.Next() && txNum > 0; txNum-- {
 -              annotatedTx := &query.AnnotatedTx{}
 -              if err := json.Unmarshal(itr.Value(), &annotatedTx); err != nil {
 -                      return nil, err
 -              }
 -
 +      newAnnotatedTxs := []*query.AnnotatedTx{}
 +      for _, annotatedTx := range annotatedTxs {
                if accountID == "" || findTransactionsByAccount(annotatedTx, accountID) {
                        annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
 -                      annotatedTxs = append([]*query.AnnotatedTx{annotatedTx}, annotatedTxs...)
 +                      newAnnotatedTxs = append([]*query.AnnotatedTx{annotatedTx}, newAnnotatedTxs...)
                }
        }
  
        if unconfirmed {
                sort.Sort(SortByTimestamp(annotatedTxs))
+       } else {
+               sort.Sort(SortByHeight(annotatedTxs))
        }
  
 -      return annotatedTxs, nil
 +      return newAnnotatedTxs, nil
  }
  
  // GetAccountBalances return all account balances
diff --combined wallet/unconfirmed.go
@@@ -1,14 -1,14 +1,14 @@@
  package wallet
  
  import (
 -      "encoding/json"
        "fmt"
        "sort"
        "time"
  
 +      "github.com/vapor/protocol/bc"
 +
        log "github.com/sirupsen/logrus"
  
 -      "github.com/vapor/account"
        "github.com/vapor/blockchain/query"
        "github.com/vapor/crypto/sha3pool"
        "github.com/vapor/protocol"
@@@ -22,6 -22,10 +22,6 @@@ const 
        MaxUnconfirmedTxDuration = 24 * time.Hour
  )
  
 -func calcUnconfirmedTxKey(formatKey string) []byte {
 -      return []byte(UnconfirmedTxPrefix + formatKey)
 -}
 -
  // SortByTimestamp implements sort.Interface for AnnotatedTx slices
  type SortByTimestamp []*query.AnnotatedTx
  
@@@ -29,6 -33,13 +29,13 @@@ func (a SortByTimestamp) Len() in
  func (a SortByTimestamp) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
  func (a SortByTimestamp) Less(i, j int) bool { return a[i].Timestamp > a[j].Timestamp }
  
+ // SortByHeight implements sort.Interface for AnnotatedTx slices
+ type SortByHeight []*query.AnnotatedTx
+ func (a SortByHeight) Len() int           { return len(a) }
+ func (a SortByHeight) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
+ func (a SortByHeight) Less(i, j int) bool { return a[i].BlockHeight > a[j].BlockHeight }
  // AddUnconfirmedTx handle wallet status update when tx add into txpool
  func (w *Wallet) AddUnconfirmedTx(txD *protocol.TxDesc) {
        if err := w.saveUnconfirmedTx(txD.Tx); err != nil {
  // GetUnconfirmedTxs get account unconfirmed transactions, filter transactions by accountID when accountID is not empty
  func (w *Wallet) GetUnconfirmedTxs(accountID string) ([]*query.AnnotatedTx, error) {
        annotatedTxs := []*query.AnnotatedTx{}
 -      txIter := w.DB.IteratorPrefix([]byte(UnconfirmedTxPrefix))
 -      defer txIter.Release()
 -
 -      for txIter.Next() {
 -              annotatedTx := &query.AnnotatedTx{}
 -              if err := json.Unmarshal(txIter.Value(), &annotatedTx); err != nil {
 -                      return nil, err
 -              }
 +      annotatedTxs, err := w.store.ListUnconfirmedTransactions()
 +      if err != nil {
 +              return nil, err
 +      }
  
 +      newAnnotatedTxs := []*query.AnnotatedTx{}
 +      for _, annotatedTx := range annotatedTxs {
                if accountID == "" || findTransactionsByAccount(annotatedTx, accountID) {
                        annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
 -                      annotatedTxs = append([]*query.AnnotatedTx{annotatedTx}, annotatedTxs...)
 +                      newAnnotatedTxs = append([]*query.AnnotatedTx{annotatedTx}, newAnnotatedTxs...)
                }
        }
  
 -      sort.Sort(SortByTimestamp(annotatedTxs))
 -      return annotatedTxs, nil
 +      sort.Sort(SortByTimestamp(newAnnotatedTxs))
 +      return newAnnotatedTxs, nil
  }
  
  // GetUnconfirmedTxByTxID get unconfirmed transaction by txID
  func (w *Wallet) GetUnconfirmedTxByTxID(txID string) (*query.AnnotatedTx, error) {
 -      annotatedTx := &query.AnnotatedTx{}
 -      txInfo := w.DB.Get(calcUnconfirmedTxKey(txID))
 -      if txInfo == nil {
 -              return nil, fmt.Errorf("No transaction(tx_id=%s) from txpool", txID)
 -      }
 -
 -      if err := json.Unmarshal(txInfo, annotatedTx); err != nil {
 +      annotatedTx, err := w.store.GetUnconfirmedTransaction(txID)
 +      if err != nil {
                return nil, err
        }
 -
 +      if annotatedTx == nil {
 +              return nil, fmt.Errorf("No transaction(tx_id=%s) from txpool", txID)
 +      }
        annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
        return annotatedTx, nil
  }
@@@ -78,7 -94,7 +85,7 @@@ func (w *Wallet) RemoveUnconfirmedTx(tx
        if !w.checkRelatedTransaction(txD.Tx) {
                return
        }
 -      w.DB.Delete(calcUnconfirmedTxKey(txD.Tx.ID.String()))
 +      w.store.DeleteUnconfirmedTransaction(txD.Tx.ID.String())
        w.AccountMgr.RemoveUnconfirmedUtxo(txD.Tx.ResultIds)
  }
  
@@@ -105,12 -121,7 +112,12 @@@ func (w *Wallet) checkRelatedTransactio
        for _, v := range tx.Outputs {
                var hash [32]byte
                sha3pool.Sum256(hash[:], v.ControlProgram())
 -              if bytes := w.DB.Get(account.ContractKey(hash)); bytes != nil {
 +              cp, err := w.store.GetControlProgram(bc.NewHash(hash))
 +              if err != nil {
 +                      log.WithFields(log.Fields{"module": logModule, "err": err, "hash": string(hash[:])}).Error("checkRelatedTransaction fail.")
 +                      continue
 +              }
 +              if cp != nil {
                        return true
                }
        }
                if err != nil {
                        continue
                }
 -              if bytes := w.DB.Get(account.StandardUTXOKey(outid)); bytes != nil {
 +              utxo, err := w.store.GetStandardUTXO(outid)
 +              if err != nil {
 +                      log.WithFields(log.Fields{"module": logModule, "err": err, "outputID": outid.String()}).Error("checkRelatedTransaction fail.")
 +                      continue
 +              }
 +              if utxo != nil {
                        return true
                }
        }
@@@ -142,11 -148,14 +149,11 @@@ func (w *Wallet) saveUnconfirmedTx(tx *
        annotatedTx := w.buildAnnotatedUnconfirmedTx(tx)
        annotatedTxs := []*query.AnnotatedTx{}
        annotatedTxs = append(annotatedTxs, annotatedTx)
 -      annotateTxsAccount(annotatedTxs, w.DB)
 +      annotateTxsAccount(annotatedTxs, w.store)
  
 -      rawTx, err := json.Marshal(annotatedTxs[0])
 -      if err != nil {
 +      if err := w.store.SetUnconfirmedTransaction(tx.ID.String(), annotatedTxs[0]); err != nil {
                return err
        }
 -
 -      w.DB.Set(calcUnconfirmedTxKey(tx.ID.String()), rawTx)
        return nil
  }
  
@@@ -157,7 -166,7 +164,7 @@@ func (w *Wallet) delExpiredTxs() error 
        }
        for _, tx := range AnnotatedTx {
                if time.Now().After(time.Unix(int64(tx.Timestamp), 0).Add(MaxUnconfirmedTxDuration)) {
 -                      w.DB.Delete(calcUnconfirmedTxKey(tx.ID.String()))
 +                      w.store.DeleteUnconfirmedTransaction(tx.ID.String())
                }
        }
        return nil