OSDN Git Service

Dev fix control (#1462)
[bytom/bytom.git] / wallet / indexer.go
index 9552117..f2b1340 100644 (file)
@@ -11,34 +11,12 @@ import (
        "github.com/bytom/account"
        "github.com/bytom/asset"
        "github.com/bytom/blockchain/query"
-       "github.com/bytom/consensus"
-       "github.com/bytom/consensus/segwit"
        "github.com/bytom/crypto/sha3pool"
        chainjson "github.com/bytom/encoding/json"
-       "github.com/bytom/errors"
        "github.com/bytom/protocol/bc"
        "github.com/bytom/protocol/bc/types"
 )
 
-type rawOutput struct {
-       OutputID       bc.Hash
-       bc.AssetAmount
-       ControlProgram []byte
-       txHash         bc.Hash
-       outputIndex    uint32
-       sourceID       bc.Hash
-       sourcePos      uint64
-       ValidHeight    uint64
-}
-
-type accountOutput struct {
-       rawOutput
-       AccountID string
-       Address   string
-       keyIndex  uint64
-       change    bool
-}
-
 const (
        //TxPrefix is wallet database transactions prefix
        TxPrefix = "TXS:"
@@ -65,85 +43,17 @@ func calcTxIndexKey(txID string) []byte {
 // 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 {
-                       // delete index
                        batch.Delete(calcTxIndexKey(tmpTx.ID.String()))
                }
-
                batch.Delete(txIter.Key())
        }
 }
 
-// ReverseAccountUTXOs process the invalid blocks when orphan block rollback
-func (w *Wallet) reverseAccountUTXOs(batch db.Batch, b *types.Block, txStatus *bc.TransactionStatus) {
-       var err error
-
-       // unknow how many spent and retire outputs
-       reverseOuts := []*rawOutput{}
-
-       // handle spent UTXOs
-       for txIndex, tx := range b.Transactions {
-               for _, inpID := range tx.Tx.InputIDs {
-                       // spend and retire
-                       sp, err := tx.Spend(inpID)
-                       if err != nil {
-                               continue
-                       }
-
-                       resOut, ok := tx.Entries[*sp.SpentOutputId].(*bc.Output)
-                       if !ok {
-                               continue
-                       }
-
-                       statusFail, _ := txStatus.GetStatus(txIndex)
-                       if statusFail && *resOut.Source.Value.AssetId != *consensus.BTMAssetID {
-                               continue
-                       }
-
-                       out := &rawOutput{
-                               OutputID:       *sp.SpentOutputId,
-                               AssetAmount:    *resOut.Source.Value,
-                               ControlProgram: resOut.ControlProgram.Code,
-                               txHash:         tx.ID,
-                               sourceID:       *resOut.Source.Ref,
-                               sourcePos:      resOut.Source.Position,
-                       }
-                       reverseOuts = append(reverseOuts, out)
-               }
-       }
-
-       accOuts := loadAccountInfo(reverseOuts, w)
-       if err = upsertConfirmedAccountOutputs(accOuts, batch); err != nil {
-               log.WithField("err", err).Error("reversing account spent and retire outputs")
-               return
-       }
-
-       // handle new UTXOs
-       for _, tx := range b.Transactions {
-               for j := range tx.Outputs {
-                       resOutID := tx.ResultIds[j]
-                       resOut, ok := tx.Entries[*resOutID].(*bc.Output)
-                       if !ok {
-                               // retirement
-                               continue
-                       }
-
-                       if segwit.IsP2WScript(resOut.ControlProgram.Code) {
-                               // delete standard UTXOs
-                               batch.Delete(account.StandardUTXOKey(*resOutID))
-                       } else {
-                               // delete contract UTXOs
-                               batch.Delete(account.ContractUTXOKey(*resOutID))
-                       }
-               }
-       }
-}
-
 // saveExternalAssetDefinition save external and local assets definition,
 // when query ,query local first and if have no then query external
 // details see getAliasDefinition
@@ -156,8 +66,8 @@ func saveExternalAssetDefinition(b *types.Block, walletDB db.DB) {
                        if ii, ok := orig.TypedInput.(*types.IssuanceInput); ok {
                                if isValidJSON(ii.AssetDefinition) {
                                        assetID := ii.AssetID()
-                                       if assetExist := walletDB.Get(asset.CalcExtAssetKey(&assetID)); assetExist == nil {
-                                               storeBatch.Set(asset.CalcExtAssetKey(&assetID), ii.AssetDefinition)
+                                       if assetExist := walletDB.Get(asset.ExtAssetKey(&assetID)); assetExist == nil {
+                                               storeBatch.Set(asset.ExtAssetKey(&assetID), ii.AssetDefinition)
                                        }
                                }
                        }
@@ -199,178 +109,9 @@ func (w *Wallet) indexTransactions(batch db.Batch, b *types.Block, txStatus *bc.
 
                batch.Set(calcAnnotatedKey(formatKey(b.Height, uint32(tx.Position))), rawTx)
                batch.Set(calcTxIndexKey(tx.ID.String()), []byte(formatKey(b.Height, uint32(tx.Position))))
-       }
-       return nil
-}
-
-// buildAccountUTXOs process valid blocks to build account unspent outputs db
-func (w *Wallet) buildAccountUTXOs(batch db.Batch, b *types.Block, txStatus *bc.TransactionStatus) {
-       // get the spent UTXOs and delete the UTXOs from DB
-       prevoutDBKeys(batch, b, txStatus)
-
-       // handle new UTXOs
-       var outs []*rawOutput
-       for txIndex, tx := range b.Transactions {
-               for i, out := range tx.Outputs {
-                       resOutID := tx.ResultIds[i]
-                       resOut, ok := tx.Entries[*resOutID].(*bc.Output)
-                       if !ok {
-                               continue
-                       }
-
-                       if statusFail, _ := txStatus.GetStatus(txIndex); statusFail && *resOut.Source.Value.AssetId != *consensus.BTMAssetID {
-                               continue
-                       }
-
-                       out := &rawOutput{
-                               OutputID:       *tx.OutputID(i),
-                               AssetAmount:    out.AssetAmount,
-                               ControlProgram: out.ControlProgram,
-                               txHash:         tx.ID,
-                               outputIndex:    uint32(i),
-                               sourceID:       *resOut.Source.Ref,
-                               sourcePos:      resOut.Source.Position,
-                       }
-
-                       // coinbase utxo valid height
-                       if txIndex == 0 {
-                               out.ValidHeight = b.Height + consensus.CoinbasePendingBlockNumber
-                       }
-                       outs = append(outs, out)
-               }
-       }
-       accOuts := loadAccountInfo(outs, w)
-
-       if err := upsertConfirmedAccountOutputs(accOuts, batch); err != nil {
-               log.WithField("err", err).Error("building new account outputs")
-       }
-}
-
-func prevoutDBKeys(batch db.Batch, b *types.Block, txStatus *bc.TransactionStatus) {
-       for txIndex, tx := range b.Transactions {
-               for _, inpID := range tx.Tx.InputIDs {
-                       sp, err := tx.Spend(inpID)
-                       if err != nil {
-                               continue
-                       }
-
-                       statusFail, _ := txStatus.GetStatus(txIndex)
-                       if statusFail && *sp.WitnessDestination.Value.AssetId != *consensus.BTMAssetID {
-                               continue
-                       }
-
-                       resOut, ok := tx.Entries[*sp.SpentOutputId].(*bc.Output)
-                       if !ok {
-                               // retirement
-                               log.WithField("SpentOutputId", *sp.SpentOutputId).Info("the OutputId is retirement")
-                               continue
-                       }
-
-                       if segwit.IsP2WScript(resOut.ControlProgram.Code) {
-                               // delete standard UTXOs
-                               batch.Delete(account.StandardUTXOKey(*sp.SpentOutputId))
-                       } else {
-                               // delete contract UTXOs
-                               batch.Delete(account.ContractUTXOKey(*sp.SpentOutputId))
-                       }
-               }
-       }
-       return
-}
-
-// loadAccountInfo turns a set of output IDs into a set of
-// outputs by adding account annotations.  Outputs that can't be
-// annotated are excluded from the result.
-func loadAccountInfo(outs []*rawOutput, w *Wallet) []*accountOutput {
-       outsByScript := make(map[string][]*rawOutput, len(outs))
-       for _, out := range outs {
-               scriptStr := string(out.ControlProgram)
-               outsByScript[scriptStr] = append(outsByScript[scriptStr], out)
-       }
-
-       result := make([]*accountOutput, 0, len(outs))
-       cp := account.CtrlProgram{}
-
-       var hash [32]byte
-       for s := range outsByScript {
-               // smart contract UTXO
-               if !segwit.IsP2WScript([]byte(s)) {
-                       for _, out := range outsByScript[s] {
-                               newOut := &accountOutput{
-                                       rawOutput: *out,
-                                       change:    false,
-                               }
-                               result = append(result, newOut)
-                       }
-
-                       continue
-               }
-
-               sha3pool.Sum256(hash[:], []byte(s))
-               bytes := w.DB.Get(account.CPKey(hash))
-               if bytes == nil {
-                       continue
-               }
-
-               err := json.Unmarshal(bytes, &cp)
-               if err != nil {
-                       continue
-               }
-
-               isExist := w.DB.Get(account.Key(cp.AccountID))
-               if isExist == nil {
-                       continue
-               }
-
-               for _, out := range outsByScript[s] {
-                       newOut := &accountOutput{
-                               rawOutput: *out,
-                               AccountID: cp.AccountID,
-                               Address:   cp.Address,
-                               keyIndex:  cp.KeyIndex,
-                               change:    cp.Change,
-                       }
-                       result = append(result, newOut)
-               }
-       }
-
-       return result
-}
-
-// upsertConfirmedAccountOutputs records the account data for confirmed utxos.
-// If the account utxo already exists (because it's from a local tx), the
-// block confirmation data will in the row will be updated.
-func upsertConfirmedAccountOutputs(outs []*accountOutput, batch db.Batch) error {
-       var u *account.UTXO
-
-       for _, out := range outs {
-               u = &account.UTXO{
-                       OutputID:            out.OutputID,
-                       SourceID:            out.sourceID,
-                       AssetID:             *out.AssetId,
-                       Amount:              out.Amount,
-                       SourcePos:           out.sourcePos,
-                       ControlProgram:      out.ControlProgram,
-                       ControlProgramIndex: out.keyIndex,
-                       AccountID:           out.AccountID,
-                       Address:             out.Address,
-                       ValidHeight:         out.ValidHeight,
-                       Change:              out.change,
-               }
-
-               data, err := json.Marshal(u)
-               if err != nil {
-                       return errors.Wrap(err, "failed marshal accountutxo")
-               }
-
-               if segwit.IsP2WScript(out.ControlProgram) {
-                       // standard UTXOs
-                       batch.Set(account.StandardUTXOKey(out.OutputID), data)
-               } else {
-                       // contract UTXOs
-                       batch.Set(account.ContractUTXOKey(out.OutputID), data)
-               }
 
+               // delete unconfirmed transaction
+               batch.Delete(calcUnconfirmedTxKey(tx.ID.String()))
        }
        return nil
 }
@@ -385,7 +126,8 @@ transactionLoop:
                for _, v := range tx.Outputs {
                        var hash [32]byte
                        sha3pool.Sum256(hash[:], v.ControlProgram)
-                       if bytes := w.DB.Get(account.CPKey(hash)); bytes != nil {
+
+                       if bytes := w.DB.Get(account.ContractKey(hash)); bytes != nil {
                                annotatedTxs = append(annotatedTxs, w.buildAnnotatedTransaction(tx, b, statusFail, pos))
                                continue transactionLoop
                        }
@@ -418,37 +160,11 @@ func (w *Wallet) GetTransactionByTxID(txID string) (*query.AnnotatedTx, error) {
        if err := json.Unmarshal(txInfo, annotatedTx); err != nil {
                return nil, err
        }
+       annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
 
        return annotatedTx, nil
 }
 
-// GetTransactionsByTxID get account txs by account tx ID
-func (w *Wallet) GetTransactionsByTxID(txID string) ([]*query.AnnotatedTx, error) {
-       annotatedTxs := []*query.AnnotatedTx{}
-       formatKey := ""
-
-       if txID != "" {
-               rawFormatKey := w.DB.Get(calcTxIndexKey(txID))
-               if rawFormatKey == nil {
-                       return nil, fmt.Errorf("No transaction(txid=%s) ", txID)
-               }
-               formatKey = string(rawFormatKey)
-       }
-
-       txIter := w.DB.IteratorPrefix(calcAnnotatedKey(formatKey))
-       defer txIter.Release()
-       for txIter.Next() {
-               annotatedTx := &query.AnnotatedTx{}
-               if err := json.Unmarshal(txIter.Value(), annotatedTx); err != nil {
-                       return nil, err
-               }
-               annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
-               annotatedTxs = append([]*query.AnnotatedTx{annotatedTx}, annotatedTxs...)
-       }
-
-       return annotatedTxs, nil
-}
-
 // GetTransactionsSummary get transactions summary
 func (w *Wallet) GetTransactionsSummary(transactions []*query.AnnotatedTx) []TxSummary {
        Txs := []TxSummary{}
@@ -501,8 +217,8 @@ func findTransactionsByAccount(annotatedTx *query.AnnotatedTx, accountID string)
        return false
 }
 
-// GetTransactionsByAccountID get account txs by account ID
-func (w *Wallet) GetTransactionsByAccountID(accountID string) ([]*query.AnnotatedTx, error) {
+// GetTransactions get all walletDB transactions, and filter transactions by accountID optional
+func (w *Wallet) GetTransactions(accountID string) ([]*query.AnnotatedTx, error) {
        annotatedTxs := []*query.AnnotatedTx{}
 
        txIter := w.DB.IteratorPrefix([]byte(TxPrefix))
@@ -513,52 +229,33 @@ func (w *Wallet) GetTransactionsByAccountID(accountID string) ([]*query.Annotate
                        return nil, err
                }
 
-               if findTransactionsByAccount(annotatedTx, accountID) {
+               if accountID == "" || findTransactionsByAccount(annotatedTx, accountID) {
                        annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
-                       annotatedTxs = append(annotatedTxs, annotatedTx)
+                       annotatedTxs = append([]*query.AnnotatedTx{annotatedTx}, annotatedTxs...)
                }
        }
 
        return annotatedTxs, nil
 }
 
-// GetAccountUTXOs return all account unspent outputs
-func (w *Wallet) GetAccountUTXOs(id string) []account.UTXO {
-       var accountUTXOs []account.UTXO
-
-       accountUTXOIter := w.DB.IteratorPrefix([]byte(account.UTXOPreFix + id))
-       defer accountUTXOIter.Release()
-       for accountUTXOIter.Next() {
-               accountUTXO := account.UTXO{}
-               if err := json.Unmarshal(accountUTXOIter.Value(), &accountUTXO); err != nil {
-                       hashKey := accountUTXOIter.Key()[len(account.UTXOPreFix):]
-                       log.WithField("UTXO hash", string(hashKey)).Warn("get account UTXO")
-               } else {
-                       accountUTXOs = append(accountUTXOs, accountUTXO)
-               }
-       }
-
-       return accountUTXOs
-}
-
 // GetAccountBalances return all account balances
-func (w *Wallet) GetAccountBalances(id string) []AccountBalance {
-       return w.indexBalances(w.GetAccountUTXOs(""))
+func (w *Wallet) GetAccountBalances(accountID string, id string) ([]AccountBalance, error) {
+       return w.indexBalances(w.GetAccountUtxos(accountID, "", false, false))
 }
 
 // AccountBalance account balance
 type AccountBalance struct {
-       AccountID  string `json:"account_id"`
-       Alias      string `json:"account_alias"`
-       AssetAlias string `json:"asset_alias"`
-       AssetID    string `json:"asset_id"`
-       Amount     uint64 `json:"amount"`
+       AccountID       string                 `json:"account_id"`
+       Alias           string                 `json:"account_alias"`
+       AssetAlias      string                 `json:"asset_alias"`
+       AssetID         string                 `json:"asset_id"`
+       Amount          uint64                 `json:"amount"`
+       AssetDefinition map[string]interface{} `json:"asset_definition"`
 }
 
-func (w *Wallet) indexBalances(accountUTXOs []account.UTXO) []AccountBalance {
+func (w *Wallet) indexBalances(accountUTXOs []*account.UTXO) ([]AccountBalance, error) {
        accBalance := make(map[string]map[string]uint64)
-       balances := make([]AccountBalance, 0)
-       tmpBalance := AccountBalance{}
+       balances := []AccountBalance{}
 
        for _, accountUTXO := range accountUTXOs {
                assetID := accountUTXO.AssetID.String()
@@ -588,15 +285,22 @@ func (w *Wallet) indexBalances(accountUTXOs []account.UTXO) []AccountBalance {
 
                for _, assetID := range sortedAsset {
                        alias := w.AccountMgr.GetAliasByID(id)
-                       assetAlias := w.AssetReg.GetAliasByID(assetID)
-                       tmpBalance.Alias = alias
-                       tmpBalance.AccountID = id
-                       tmpBalance.AssetID = assetID
-                       tmpBalance.AssetAlias = assetAlias
-                       tmpBalance.Amount = accBalance[id][assetID]
-                       balances = append(balances, tmpBalance)
+                       targetAsset, err := w.AssetReg.GetAsset(assetID)
+                       if err != nil {
+                               return nil, err
+                       }
+
+                       assetAlias := *targetAsset.Alias
+                       balances = append(balances, AccountBalance{
+                               Alias:           alias,
+                               AccountID:       id,
+                               AssetID:         assetID,
+                               AssetAlias:      assetAlias,
+                               Amount:          accBalance[id][assetID],
+                               AssetDefinition: targetAsset.DefinitionMap,
+                       })
                }
        }
 
-       return balances
+       return balances, nil
 }