X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=wallet%2Findexer.go;h=f2b13405199551827f8425b5481e1efd90e70f79;hb=852dcb035f92296f653b2417633be25f54c64a91;hp=5f8b9dfc1e90796baea86a296f20fc7f1fabef37;hpb=6e130176e5ad7826ea92f805960f03ad877a4dc1;p=bytom%2Fbytom.git diff --git a/wallet/indexer.go b/wallet/indexer.go index 5f8b9dfc..f2b13405 100644 --- a/wallet/indexer.go +++ b/wallet/indexer.go @@ -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) } } } @@ -188,7 +98,6 @@ type TxSummary struct { func (w *Wallet) indexTransactions(batch db.Batch, b *types.Block, txStatus *bc.TransactionStatus) error { annotatedTxs := w.filterAccountTxs(b, txStatus) saveExternalAssetDefinition(b, w.DB) - annotateTxsAsset(w, annotatedTxs) annotateTxsAccount(annotatedTxs, w.DB) for _, tx := range annotatedTxs { @@ -200,177 +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, - } - - 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,36 +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) { - var 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 - } - annotatedTxs = append([]*query.AnnotatedTx{annotatedTx}, annotatedTxs...) - } - - return annotatedTxs, nil -} - // GetTransactionsSummary get transactions summary func (w *Wallet) GetTransactionsSummary(transactions []*query.AnnotatedTx) []TxSummary { Txs := []TxSummary{} @@ -500,9 +217,9 @@ 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) { - var annotatedTxs []*query.AnnotatedTx +// 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)) defer txIter.Release() @@ -512,51 +229,33 @@ func (w *Wallet) GetTransactionsByAccountID(accountID string) ([]*query.Annotate return nil, err } - if findTransactionsByAccount(annotatedTx, accountID) { - annotatedTxs = append(annotatedTxs, annotatedTx) + if accountID == "" || findTransactionsByAccount(annotatedTx, accountID) { + annotateTxsAsset(w, []*query.AnnotatedTx{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() @@ -586,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 }