OSDN Git Service

update
[bytom/vapor.git] / database / wallet_store.go
index 13dce11..ff25b4d 100644 (file)
@@ -4,111 +4,108 @@ import (
        "encoding/binary"
        "encoding/json"
        "fmt"
+       "sort"
 
+       acc "github.com/vapor/account"
        "github.com/vapor/asset"
        "github.com/vapor/blockchain/query"
-       "github.com/vapor/common"
+       "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"
 )
 
-var errAccntTxIDNotFound = errors.New("account TXID not found")
-
 const (
-       UTXOPrefix          = "ACU:" //UTXOPrefix is StandardUTXOKey prefix
-       SUTXOPrefix         = "SCU:" //SUTXOPrefix is ContractUTXOKey prefix
-       contractPrefix      = "Contract:"
-       accountPrefix       = "Account:"
-       TxPrefix            = "TXS:"  //TxPrefix is wallet database transactions prefix
-       TxIndexPrefix       = "TID:"  //TxIndexPrefix is wallet database tx index prefix
-       UnconfirmedTxPrefix = "UTXS:" //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
-       GlobalTxIndexPrefix = "GTID:" //GlobalTxIndexPrefix is wallet database global tx index prefix
-       walletKey           = "walletInfo"
+       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
 )
 
-// WalletStorer interface contains wallet storage functions.
-type WalletStorer interface {
-       InitBatch()
-       CommitBatch()
-       GetAssetDefinition(*bc.AssetID) []byte
-       SetAssetDefinition(*bc.AssetID, []byte)
-       GetRawProgram(common.Hash) []byte
-       GetAccountByAccountID(string) []byte
-       DeleteTransactions(uint64)
-       SetTransaction(uint64, uint32, string, []byte)
-       DeleteUnconfirmedTransaction(string)
-       SetGlobalTransactionIndex(string, *bc.Hash, uint64)
-       GetStandardUTXO(bc.Hash) []byte
-       GetTransaction(string) ([]byte, error)
-       GetGlobalTransaction(string) []byte
-       GetTransactions() ([]*query.AnnotatedTx, error)
-       GetUnconfirmedTransactions() ([]*query.AnnotatedTx, error)
-       GetUnconfirmedTransaction(string) []byte
-       SetUnconfirmedTransaction(string, []byte)
-       DeleteStardardUTXO(bc.Hash)
-       DeleteContractUTXO(bc.Hash)
-       SetStandardUTXO(bc.Hash, []byte)
-       SetContractUTXO(bc.Hash, []byte)
-       GetWalletInfo() []byte
-       SetWalletInfo([]byte)
-       DeleteWalletTransactions()
-       DeleteWalletUTXOs()
-       GetAccountUTXOs(key string) [][]byte
-       SetRecoveryStatus([]byte, []byte)
-       DeleteRecoveryStatus([]byte)
-       GetRecoveryStatus([]byte) []byte
-}
-
-// WalletStore store wallet using leveldb
-type WalletStore struct {
-       walletDB dbm.DB
-       batch    dbm.Batch
-}
+// 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}
+)
 
-// NewWalletStore create new WalletStore struct
-func NewWalletStore(db dbm.DB) *WalletStore {
-       return &WalletStore{
-               walletDB: db,
-               batch:    nil,
-       }
-}
+// errors
+var (
+       // ErrFindAccount        = errors.New("Failed to find account")
+       errAccntTxIDNotFound = errors.New("account TXID not found")
+       errGetAsset          = errors.New("Failed to find asset definition")
+)
 
-// InitBatch initial batch
-func (store *WalletStore) InitBatch() {
-       if store.batch == nil {
-               store.batch = store.walletDB.NewBatch()
+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[:]...)
 }
 
-// CommitBatch commit batch
-func (store *WalletStore) CommitBatch() {
-       if store.batch != nil {
-               store.batch.Write()
-               store.batch = nil
+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 common.Hash) []byte {
-       return append([]byte(contractPrefix), hash[:]...)
+func ContractKey(hash bc.Hash) []byte {
+       // h := hash.String()
+       // return append([]byte(ContractPrefix), []byte(h)...)
+       return append([]byte(ContractPrefix), hash.Bytes()...)
 }
 
-// Key account store prefix
-func Key(name string) []byte {
-       return append([]byte(accountPrefix), []byte(name)...)
+// 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 []byte(UTXOPrefix + name)
+       // name := id.String()
+       // return append(UTXOPrefix, []byte(name)...)
+       return append(UTXOPrefix, id.Bytes()...)
 }
 
 // ContractUTXOKey makes a smart contract unspent outputs key to store
 func ContractUTXOKey(id bc.Hash) []byte {
-       name := id.String()
-       return []byte(SUTXOPrefix + name)
+       // name := id.String()
+       // return append(SUTXOPrefix, []byte(name)...)
+       return append(SUTXOPrefix, id.Bytes()...)
 }
 
 func calcDeleteKey(blockHeight uint64) []byte {
@@ -116,19 +113,19 @@ func calcDeleteKey(blockHeight uint64) []byte {
 }
 
 func calcTxIndexKey(txID string) []byte {
-       return []byte(TxIndexPrefix + txID)
+       return append(TxIndexPrefix, []byte(txID)...)
 }
 
 func calcAnnotatedKey(formatKey string) []byte {
-       return []byte(TxPrefix + formatKey)
+       return append(TxPrefix, []byte(formatKey)...)
 }
 
 func calcUnconfirmedTxKey(formatKey string) []byte {
-       return []byte(UnconfirmedTxPrefix + formatKey)
+       return append(UnconfirmedTxPrefix, []byte(formatKey)...)
 }
 
 func calcGlobalTxIndexKey(txID string) []byte {
-       return []byte(GlobalTxIndexPrefix + txID)
+       return append(GlobalTxIndexPrefix, []byte(txID)...)
 }
 
 func CalcGlobalTxIndex(blockHash *bc.Hash, position uint64) []byte {
@@ -142,33 +139,67 @@ func formatKey(blockHeight uint64, position uint32) string {
        return fmt.Sprintf("%016x%08x", blockHeight, position)
 }
 
-// GetAssetDefinition get asset definition by assetiD
-func (store *WalletStore) GetAssetDefinition(assetID *bc.AssetID) []byte {
-       return store.walletDB.Get(asset.ExtAssetKey(assetID))
+func contractIndexKey(accountID string) []byte {
+       return append([]byte(ContractIndexPrefix), []byte(accountID)...)
 }
 
-// SetAssetDefinition set assetID and definition
-func (store *WalletStore) SetAssetDefinition(assetID *bc.AssetID, definition []byte) {
+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 {
-               store.walletDB.Set(asset.ExtAssetKey(assetID), definition)
-       } else {
-               store.batch.Set(asset.ExtAssetKey(assetID), definition)
+               return errors.New("WalletStore commit fail, store batch is nil.")
        }
+       store.batch.Write()
+       store.batch = nil
+       return nil
 }
 
-// GetRawProgram get raw program by hash
-func (store *WalletStore) GetRawProgram(hash common.Hash) []byte {
-       return store.walletDB.Get(ContractKey(hash))
+// 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))
+       }
 }
 
-// GetAccountByAccountID get account value by account ID
-func (store *WalletStore) GetAccountByAccountID(accountID string) []byte {
-       return store.walletDB.Get(Key(accountID))
+// DeleteRecoveryStatus delete recovery status
+func (store *WalletStore) DeleteRecoveryStatus() {
+       if store.batch == nil {
+               store.walletDB.Delete(RecoveryKey)
+       } else {
+               store.batch.Delete(RecoveryKey)
+       }
 }
 
 // DeleteTransactions delete transactions when orphan block rollback
 func (store *WalletStore) DeleteTransactions(height uint64) {
-       tmpTx := query.AnnotatedTx{}
        batch := store.walletDB.NewBatch()
        if store.batch != nil {
                batch = store.batch
@@ -176,6 +207,7 @@ func (store *WalletStore) DeleteTransactions(height uint64) {
        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()))
@@ -187,68 +219,193 @@ func (store *WalletStore) DeleteTransactions(height uint64) {
        }
 }
 
-// SetTransaction set raw transaction by block height and tx position
-func (store *WalletStore) SetTransaction(height uint64, position uint32, txID string, rawTx []byte) {
+// 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())
+       }
 
-       batch.Set(calcAnnotatedKey(formatKey(height, position)), rawTx)
-       batch.Set(calcTxIndexKey(txID), []byte(formatKey(height, position)))
+       txIndexIter := store.walletDB.IteratorPrefix([]byte(TxIndexPrefix))
+       defer txIndexIter.Release()
 
+       for txIndexIter.Next() {
+               batch.Delete(txIndexIter.Key())
+       }
        if store.batch == nil {
                batch.Write()
        }
 }
 
-// DeleteUnconfirmedTransaction delete unconfirmed tx by txID
-func (store *WalletStore) DeleteUnconfirmedTransaction(txID string) {
+// 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 {
-               store.walletDB.Delete(calcUnconfirmedTxKey(txID))
-       } else {
-               store.batch.Delete(calcUnconfirmedTxKey(txID))
+               batch.Write()
        }
 }
 
-// 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))
+// 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, acc.ErrFindCtrlProgram
+       }
+       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) []byte {
-       return store.walletDB.Get(StandardUTXOKey(outid))
+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 tx index
-func (store *WalletStore) GetTransaction(txID string) ([]byte, error) {
+// 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
        }
-       txInfo := store.walletDB.Get(calcAnnotatedKey(string(formatKey)))
-       return txInfo, nil
+       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
 }
 
-// GetGlobalTransaction get global tx by txID
-func (store *WalletStore) GetGlobalTransaction(txID string) []byte {
-       return store.walletDB.Get(calcGlobalTxIndexKey(txID))
+// 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) {
+       fmt.Println("ListAccountUTXOs []byte(key):", []byte(key))
+       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
 }
 
-// GetTransactions get all walletDB transactions
-func (store *WalletStore) GetTransactions() ([]*query.AnnotatedTx, error) {
+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))
+               }
+       }
 
-       txIter := store.walletDB.IteratorPrefix([]byte(TxPrefix))
-       defer txIter.Release()
-       for txIter.Next() {
-               annotatedTx := &query.AnnotatedTx{}
-               if err := json.Unmarshal(txIter.Value(), &annotatedTx); err != nil {
+       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)
@@ -257,8 +414,8 @@ func (store *WalletStore) GetTransactions() ([]*query.AnnotatedTx, error) {
        return annotatedTxs, nil
 }
 
-// GetUnconfirmedTransactions get all unconfirmed txs
-func (store *WalletStore) GetUnconfirmedTransactions() ([]*query.AnnotatedTx, error) {
+// ListUnconfirmedTransactions get all unconfirmed txs
+func (store *WalletStore) ListUnconfirmedTransactions() ([]*query.AnnotatedTx, error) {
        annotatedTxs := []*query.AnnotatedTx{}
        txIter := store.walletDB.IteratorPrefix([]byte(UnconfirmedTxPrefix))
        defer txIter.Release()
@@ -273,138 +430,86 @@ func (store *WalletStore) GetUnconfirmedTransactions() ([]*query.AnnotatedTx, er
        return annotatedTxs, nil
 }
 
-// GetUnconfirmedTransaction get unconfirmed tx by txID
-func (store *WalletStore) GetUnconfirmedTransaction(txID string) []byte {
-       return store.walletDB.Get(calcUnconfirmedTxKey(txID))
-}
-
-// SetUnconfirmedTransaction set unconfirmed tx by txID
-func (store *WalletStore) SetUnconfirmedTransaction(txID string, rawTx []byte) {
-       if store.batch == nil {
-               store.walletDB.Set(calcUnconfirmedTxKey(txID), rawTx)
-       } else {
-               store.batch.Set(calcUnconfirmedTxKey(txID), rawTx)
-       }
-}
-
-// DeleteStardardUTXO delete stardard utxo by outputID
-func (store *WalletStore) DeleteStardardUTXO(outputID bc.Hash) {
+// SetAssetDefinition set assetID and definition
+func (store *WalletStore) SetAssetDefinition(assetID *bc.AssetID, definition []byte) {
        if store.batch == nil {
-               store.walletDB.Delete(StandardUTXOKey(outputID))
+               store.walletDB.Set(asset.ExtAssetKey(assetID), definition)
        } else {
-               store.batch.Delete(StandardUTXOKey(outputID))
+               store.batch.Set(asset.ExtAssetKey(assetID), definition)
        }
 }
 
-// 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))
+// 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
        }
-}
-
-// SetStandardUTXO set standard utxo
-func (store *WalletStore) SetStandardUTXO(outputID bc.Hash, data []byte) {
        if store.batch == nil {
-               store.walletDB.Set(StandardUTXOKey(outputID), data)
+               store.walletDB.Set(ContractUTXOKey(outputID), data)
        } else {
-               store.batch.Set(StandardUTXOKey(outputID), data)
+               store.batch.Set(ContractUTXOKey(outputID), data)
        }
+       return nil
 }
 
-// SetContractUTXO set standard utxo
-func (store *WalletStore) SetContractUTXO(outputID bc.Hash, data []byte) {
+// 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(ContractUTXOKey(outputID), data)
+               store.walletDB.Set(calcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
        } else {
-               store.batch.Set(ContractUTXOKey(outputID), data)
+               store.batch.Set(calcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
        }
 }
 
-// GetWalletInfo get wallet information
-func (store *WalletStore) GetWalletInfo() []byte {
-       return store.walletDB.Get([]byte(walletKey))
-}
-
-// SetWalletInfo get wallet information
-func (store *WalletStore) SetWalletInfo(rawWallet []byte) {
+// SetRecoveryStatus set recovery status
+func (store *WalletStore) SetRecoveryStatus(recoveryKey, rawStatus []byte) {
        if store.batch == nil {
-               store.walletDB.Set([]byte(walletKey), rawWallet)
+               store.walletDB.Set(recoveryKey, rawStatus)
        } else {
-               store.batch.Set([]byte(walletKey), rawWallet)
+               store.batch.Set(recoveryKey, rawStatus)
        }
 }
 
-// DeleteWalletTransactions delete all txs in wallet
-func (store *WalletStore) DeleteWalletTransactions() {
+// SetTransaction set raw transaction by block height and tx position
+func (store *WalletStore) SetTransaction(height uint64, tx *query.AnnotatedTx) error {
        batch := store.walletDB.NewBatch()
-       txIter := store.walletDB.IteratorPrefix([]byte(TxPrefix))
-       defer txIter.Release()
-
-       for txIter.Next() {
-               batch.Delete(txIter.Key())
+       if store.batch != nil {
+               batch = store.batch
        }
 
-       txIndexIter := store.walletDB.IteratorPrefix([]byte(TxIndexPrefix))
-       defer txIndexIter.Release()
-
-       for txIndexIter.Next() {
-               batch.Delete(txIndexIter.Key())
+       rawTx, err := json.Marshal(tx)
+       if err != nil {
+               return err
        }
-       batch.Write()
-}
+       batch.Set(calcAnnotatedKey(formatKey(height, tx.Position)), rawTx)
+       batch.Set(calcTxIndexKey(tx.ID.String()), []byte(formatKey(height, tx.Position)))
 
-// DeleteWalletUTXOs delete all txs in wallet
-func (store *WalletStore) DeleteWalletUTXOs() {
-       batch := store.walletDB.NewBatch()
-       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()
        }
-       batch.Write()
+       return nil
 }
 
-// GetAccountUTXOs get all account unspent outputs
-func (store *WalletStore) GetAccountUTXOs(key string) [][]byte {
-       accountUtxoIter := store.walletDB.IteratorPrefix([]byte(key))
-       defer accountUtxoIter.Release()
-
-       rawUTXOs := make([][]byte, 0)
-       for accountUtxoIter.Next() {
-               utxo := accountUtxoIter.Value()
-               rawUTXOs = append(rawUTXOs, utxo)
+// 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
        }
-       return rawUTXOs
-}
-
-// SetRecoveryStatus set recovery status
-func (store *WalletStore) SetRecoveryStatus(recoveryKey, rawStatus []byte) {
        if store.batch == nil {
-               store.walletDB.Set(recoveryKey, rawStatus)
+               store.walletDB.Set(calcUnconfirmedTxKey(txID), rawTx)
        } else {
-               store.batch.Set(recoveryKey, rawStatus)
+               store.batch.Set(calcUnconfirmedTxKey(txID), rawTx)
        }
+       return nil
 }
 
-// DeleteRecoveryStatus delete recovery status
-func (store *WalletStore) DeleteRecoveryStatus(recoveryKey []byte) {
+// SetWalletInfo get wallet information
+func (store *WalletStore) SetWalletInfo(rawWallet []byte) {
        if store.batch == nil {
-               store.walletDB.Delete(recoveryKey)
+               store.walletDB.Set([]byte(WalletKey), rawWallet)
        } else {
-               store.batch.Delete(recoveryKey)
+               store.batch.Set([]byte(WalletKey), rawWallet)
        }
 }
-
-// GetRecoveryStatus delete recovery status
-func (store *WalletStore) GetRecoveryStatus(recoveryKey []byte) []byte {
-       return store.walletDB.Get(recoveryKey)
-}