--- /dev/null
- 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)
+ }
+}
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
}
// 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
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
}
}
}
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)
}
// 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
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"
MaxUnconfirmedTxDuration = 24 * time.Hour
)
-func calcUnconfirmedTxKey(formatKey string) []byte {
- return []byte(UnconfirmedTxPrefix + formatKey)
-}
-
// SortByTimestamp implements sort.Interface for AnnotatedTx slices
type SortByTimestamp []*query.AnnotatedTx
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
}
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)
}
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
}
}
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
}
}
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