OSDN Git Service

fix: use mainchain address for cross_chain_out (#131)
[bytom/vapor.git] / wallet / indexer.go
index 8ae4bac..f3b7d57 100644 (file)
@@ -1,22 +1,22 @@
 package wallet
 
 import (
-       "bytes"
+       "encoding/binary"
        "encoding/json"
        "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/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"
-       "github.com/vapor/protocol/vm/vmutil"
 )
 
 const (
@@ -24,8 +24,12 @@ const (
        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)
 }
@@ -42,8 +46,27 @@ 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])
+       hash := bc.NewHash(hashBytes)
+       position := binary.BigEndian.Uint64(globalTxIdx[32:])
+       return &hash, position
+}
+
 // deleteTransaction delete transactions when orphan block rollback
-func (w *Wallet) deleteTransactions(batch db.Batch, height uint64) {
+func (w *Wallet) deleteTransactions(batch dbm.Batch, height uint64) {
        tmpTx := query.AnnotatedTx{}
        txIter := w.DB.IteratorPrefix(calcDeleteKey(height))
        defer txIter.Release()
@@ -59,18 +82,16 @@ func (w *Wallet) deleteTransactions(batch db.Batch, height uint64) {
 // 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) {
+func saveExternalAssetDefinition(b *types.Block, walletDB dbm.DB) {
        storeBatch := walletDB.NewBatch()
        defer storeBatch.Write()
 
        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 assetExist := walletDB.Get(asset.ExtAssetKey(assetID)); assetExist == nil {
+                                       storeBatch.Set(asset.ExtAssetKey(assetID), cci.AssetDefinition)
                                }
                        }
                }
@@ -97,7 +118,7 @@ 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 {
+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)
@@ -105,7 +126,7 @@ func (w *Wallet) indexTransactions(batch db.Batch, b *types.Block, txStatus *bc.
        for _, tx := range annotatedTxs {
                rawTx, err := json.Marshal(tx)
                if err != nil {
-                       log.WithField("err", err).Error("inserting annotated_txs to db")
+                       log.WithFields(log.Fields{"module": logModule, "err": err}).Error("inserting annotated_txs to db")
                        return err
                }
 
@@ -115,26 +136,30 @@ func (w *Wallet) indexTransactions(batch db.Batch, b *types.Block, txStatus *bc.
                // delete unconfirmed transaction
                batch.Delete(calcUnconfirmedTxKey(tx.ID.String()))
        }
+
+       if !w.TxIndexFlag {
+               return nil
+       }
+
+       for position, globalTx := range b.Transactions {
+               blockHash := b.BlockHeader.Hash()
+               batch.Set(calcGlobalTxIndexKey(globalTx.ID.String()), calcGlobalTxIndex(&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)
+                       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
@@ -142,11 +167,6 @@ transactionLoop:
                }
 
                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 {
                                continue
@@ -163,21 +183,57 @@ transactionLoop:
 
 // GetTransactionByTxID get transaction by txID
 func (w *Wallet) GetTransactionByTxID(txID string) (*query.AnnotatedTx, error) {
+       if annotatedTx, err := w.getAccountTxByTxID(txID); err == nil {
+               return annotatedTx, nil
+       } else if !w.TxIndexFlag {
+               return nil, err
+       }
+
+       return w.getGlobalTxByTxID(txID)
+}
+
+func (w *Wallet) getAccountTxByTxID(txID string) (*query.AnnotatedTx, error) {
+       annotatedTx := &query.AnnotatedTx{}
        formatKey := w.DB.Get(calcTxIndexKey(txID))
        if formatKey == nil {
-               return nil, fmt.Errorf("No transaction(tx_id=%s) ", txID)
+               return nil, errAccntTxIDNotFound
        }
 
-       annotatedTx := &query.AnnotatedTx{}
        txInfo := w.DB.Get(calcAnnotatedKey(string(formatKey)))
        if err := json.Unmarshal(txInfo, annotatedTx); 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.DB.Get(calcGlobalTxIndexKey(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{}