X-Git-Url: http://git.osdn.net/view?p=bytom%2Fvapor.git;a=blobdiff_plain;f=wallet%2Findexer.go;h=9f55115aeb27d6a976a6374e9a5f26b9672cc432;hp=8ae4bacc1da9e0684436aef8377201f1762a846a;hb=489e57ce3c46eb9e8ca25c7e966a1ea26fe41d57;hpb=ddc7106558f020bde24cc337d51649611dddaba8 diff --git a/wallet/indexer.go b/wallet/indexer.go index 8ae4bacc..9f55115a 100644 --- a/wallet/indexer.go +++ b/wallet/indexer.go @@ -1,80 +1,55 @@ package wallet import ( - "bytes" - "encoding/json" + "encoding/binary" + "encoding/hex" "fmt" "sort" log "github.com/sirupsen/logrus" - "github.com/tendermint/tmlibs/db" "github.com/vapor/account" - "github.com/vapor/asset" "github.com/vapor/blockchain/query" + "github.com/vapor/consensus" "github.com/vapor/crypto/sha3pool" chainjson "github.com/vapor/encoding/json" "github.com/vapor/protocol/bc" "github.com/vapor/protocol/bc/types" - "github.com/vapor/protocol/vm/vmutil" ) -const ( - //TxPrefix is wallet database transactions prefix - TxPrefix = "TXS:" - //TxIndexPrefix is wallet database tx index prefix - TxIndexPrefix = "TID:" -) - -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) -} - -// deleteTransaction delete transactions when orphan block rollback -func (w *Wallet) deleteTransactions(batch db.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()) - } +func parseGlobalTxIdx(globalTxIdx []byte) (*bc.Hash, uint64) { + var hashBytes [32]byte + copy(hashBytes[:], globalTxIdx[:32]) + hash := bc.NewHash(hashBytes) + position := binary.BigEndian.Uint64(globalTxIdx[32:]) + return &hash, position } // 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 db.DB) { - storeBatch := walletDB.NewBatch() - defer storeBatch.Write() +func saveExternalAssetDefinition(b *types.Block, store WalletStore) error { + newStore := store.InitBatch() for _, tx := range b.Transactions { for _, orig := range tx.Inputs { - if ii, ok := orig.TypedInput.(*types.IssuanceInput); ok { - if isValidJSON(ii.AssetDefinition) { - assetID := ii.AssetID() - if assetExist := walletDB.Get(asset.ExtAssetKey(&assetID)); assetExist == nil { - storeBatch.Set(asset.ExtAssetKey(&assetID), ii.AssetDefinition) - } + if cci, ok := orig.TypedInput.(*types.CrossChainInput); ok { + assetID := cci.AssetId + if _, err := newStore.GetAsset(assetID); err == nil { + continue + } else if err != ErrGetAsset { + return err } + + newStore.SetAssetDefinition(assetID, cci.AssetDefinition) } } } + if err := newStore.CommitBatch(); err != nil { + return err + } + + return nil } // Summary is the struct of transaction's input and output summary @@ -97,63 +72,56 @@ type TxSummary struct { } // indexTransactions saves all annotated transactions to the database. -func (w *Wallet) indexTransactions(batch db.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, store WalletStore) error { for _, tx := range annotatedTxs { - rawTx, err := json.Marshal(tx) - if err != nil { - log.WithField("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)))) + store.DeleteUnconfirmedTransaction(tx.ID.String()) + } - // delete unconfirmed transaction - batch.Delete(calcUnconfirmedTxKey(tx.ID.String())) + if !w.TxIndexFlag { + return nil } + + for position, globalTx := range b.Transactions { + blockHash := b.BlockHeader.Hash() + store.SetGlobalTransactionIndex(globalTx.ID.String(), &blockHash, uint64(position)) + } + return nil } // filterAccountTxs related and build the fully annotated transactions. func (w *Wallet) filterAccountTxs(b *types.Block, txStatus *bc.TransactionStatus) []*query.AnnotatedTx { annotatedTxs := make([]*query.AnnotatedTx, 0, len(b.Transactions)) - redeemContract := w.dposAddress.ScriptAddress() - program, _ := vmutil.P2WPKHProgram(redeemContract) + transactionLoop: for pos, tx := range b.Transactions { statusFail, _ := txStatus.GetStatus(pos) for _, v := range tx.Outputs { - - if bytes.Equal(v.ControlProgram, program) { - annotatedTxs = append(annotatedTxs, w.buildAnnotatedTransaction(tx, b, statusFail, pos)) - continue transactionLoop - } - var hash [32]byte - sha3pool.Sum256(hash[:], v.ControlProgram) - if bytes := w.DB.Get(account.ContractKey(hash)); bytes != nil { + sha3pool.Sum256(hash[:], v.ControlProgram()) + if _, err := w.AccountMgr.GetControlProgram(bc.NewHash(hash)); err == nil { annotatedTxs = append(annotatedTxs, w.buildAnnotatedTransaction(tx, b, statusFail, pos)) continue transactionLoop + } else if err != account.ErrFindCtrlProgram { + log.WithFields(log.Fields{"module": logModule, "err": err, "hash": hex.EncodeToString(hash[:])}).Error("filterAccountTxs fail.") } } for _, v := range tx.Inputs { - if bytes.Equal(v.ControlProgram(), program) { - annotatedTxs = append(annotatedTxs, w.buildAnnotatedTransaction(tx, b, statusFail, pos)) - continue transactionLoop - } - 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 { + if _, err = w.Store.GetStandardUTXO(outid); err == nil { annotatedTxs = append(annotatedTxs, w.buildAnnotatedTransaction(tx, b, statusFail, pos)) continue transactionLoop + } else if err != ErrGetStandardUTXO { + log.WithFields(log.Fields{"module": logModule, "err": err, "outputID": outid.String()}).Error("filterAccountTxs fail.") } } } @@ -163,21 +131,51 @@ transactionLoop: // GetTransactionByTxID get transaction by txID func (w *Wallet) GetTransactionByTxID(txID string) (*query.AnnotatedTx, error) { - formatKey := w.DB.Get(calcTxIndexKey(txID)) - if formatKey == nil { - return nil, fmt.Errorf("No transaction(tx_id=%s) ", txID) + if annotatedTx, err := w.getAccountTxByTxID(txID); err == nil { + return annotatedTx, nil + } else if !w.TxIndexFlag { + return nil, err } - annotatedTx := &query.AnnotatedTx{} - txInfo := w.DB.Get(calcAnnotatedKey(string(formatKey))) - if err := json.Unmarshal(txInfo, annotatedTx); err != nil { + return w.getGlobalTxByTxID(txID) +} + +func (w *Wallet) getAccountTxByTxID(txID string) (*query.AnnotatedTx, error) { + annotatedTx, err := w.Store.GetTransaction(txID) + if err != nil { return nil, err } - annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx}) + annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx}) return annotatedTx, nil } +func (w *Wallet) getGlobalTxByTxID(txID string) (*query.AnnotatedTx, error) { + globalTxIdx := w.Store.GetGlobalTransactionIndex(txID) + if globalTxIdx == nil { + return nil, fmt.Errorf("No transaction(tx_id=%s) ", txID) + } + + blockHash, pos := parseGlobalTxIdx(globalTxIdx) + block, err := w.Chain.GetBlockByHash(blockHash) + if err != nil { + return nil, err + } + + txStatus, err := w.Chain.GetTransactionStatus(blockHash) + if err != nil { + return nil, err + } + + statusFail, err := txStatus.GetStatus(int(pos)) + if err != nil { + return nil, err + } + + tx := block.Transactions[int(pos)] + return w.buildAnnotatedTransaction(tx, block, statusFail, int(pos)), nil +} + // GetTransactionsSummary get transactions summary func (w *Wallet) GetTransactionsSummary(transactions []*query.AnnotatedTx) []TxSummary { Txs := []TxSummary{} @@ -230,30 +228,34 @@ func findTransactionsByAccount(annotatedTx *query.AnnotatedTx, accountID string) return false } -// GetTransactions get all walletDB transactions, and filter transactions by accountID optional -func (w *Wallet) GetTransactions(accountID string) ([]*query.AnnotatedTx, error) { +// 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{} + annotatedTxs, err := w.Store.ListTransactions(accountID, StartTxID, count, unconfirmed) + if err != nil { + return nil, err + } - txIter := w.DB.IteratorPrefix([]byte(TxPrefix)) - defer txIter.Release() - for txIter.Next() { - annotatedTx := &query.AnnotatedTx{} - if err := json.Unmarshal(txIter.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...) } } - return annotatedTxs, nil + if unconfirmed { + sort.Sort(SortByTimestamp(annotatedTxs)) + } else { + sort.Sort(SortByHeight(annotatedTxs)) + } + + return newAnnotatedTxs, nil } // GetAccountBalances return all account balances func (w *Wallet) GetAccountBalances(accountID string, id string) ([]AccountBalance, error) { - return w.indexBalances(w.GetAccountUtxos(accountID, "", false, false)) + return w.indexBalances(w.GetAccountUtxos(accountID, "", false, false, false)) } // AccountBalance account balance @@ -317,3 +319,72 @@ func (w *Wallet) indexBalances(accountUTXOs []*account.UTXO) ([]AccountBalance, return balances, nil } + +// GetAccountVotes return all account votes +func (w *Wallet) GetAccountVotes(accountID string, id string) ([]AccountVotes, error) { + return w.indexVotes(w.GetAccountUtxos(accountID, "", false, false, true)) +} + +type voteDetail struct { + Vote string `json:"vote"` + VoteNumber uint64 `json:"vote_number"` +} + +// AccountVotes account vote +type AccountVotes struct { + AccountID string `json:"account_id"` + Alias string `json:"account_alias"` + TotalVoteNumber uint64 `json:"total_vote_number"` + VoteDetails []voteDetail `json:"vote_details"` +} + +func (w *Wallet) indexVotes(accountUTXOs []*account.UTXO) ([]AccountVotes, error) { + accVote := make(map[string]map[string]uint64) + votes := []AccountVotes{} + + for _, accountUTXO := range accountUTXOs { + if accountUTXO.AssetID != *consensus.BTMAssetID || accountUTXO.Vote == nil { + continue + } + xpub := hex.EncodeToString(accountUTXO.Vote) + if _, ok := accVote[accountUTXO.AccountID]; ok { + accVote[accountUTXO.AccountID][xpub] += accountUTXO.Amount + } else { + accVote[accountUTXO.AccountID] = map[string]uint64{xpub: accountUTXO.Amount} + + } + } + + var sortedAccount []string + for k := range accVote { + sortedAccount = append(sortedAccount, k) + } + sort.Strings(sortedAccount) + + for _, id := range sortedAccount { + var sortedXpub []string + for k := range accVote[id] { + sortedXpub = append(sortedXpub, k) + } + sort.Strings(sortedXpub) + + voteDetails := []voteDetail{} + voteTotal := uint64(0) + for _, xpub := range sortedXpub { + voteDetails = append(voteDetails, voteDetail{ + Vote: xpub, + VoteNumber: accVote[id][xpub], + }) + voteTotal += accVote[id][xpub] + } + alias := w.AccountMgr.GetAliasByID(id) + votes = append(votes, AccountVotes{ + Alias: alias, + AccountID: id, + VoteDetails: voteDetails, + TotalVoteNumber: voteTotal, + }) + } + + return votes, nil +}