X-Git-Url: http://git.osdn.net/view?p=bytom%2Fvapor.git;a=blobdiff_plain;f=wallet%2Futxo.go;h=bc60f56cbd6f7d1342ea9e32567feace0ef85a7f;hp=ab9fa1eaa6c86a90d9084180f76563970cc512e6;hb=df9deca26ab5e99b48bae28fb8282ec3fa47d516;hpb=08281341c2cb02ba11d4218576256688854790fc diff --git a/wallet/utxo.go b/wallet/utxo.go index ab9fa1ea..bc60f56c 100644 --- a/wallet/utxo.go +++ b/wallet/utxo.go @@ -1,54 +1,46 @@ package wallet import ( - "encoding/json" - log "github.com/sirupsen/logrus" - "github.com/tendermint/tmlibs/db" "github.com/vapor/account" "github.com/vapor/consensus" "github.com/vapor/consensus/segwit" "github.com/vapor/crypto/sha3pool" - "github.com/vapor/errors" "github.com/vapor/protocol/bc" "github.com/vapor/protocol/bc/types" ) // GetAccountUtxos return all account unspent outputs -func (w *Wallet) GetAccountUtxos(accountID string, id string, unconfirmed, isSmartContract bool) []*account.UTXO { - prefix := account.UTXOPreFix - if isSmartContract { - prefix = account.SUTXOPrefix - } - +func (w *Wallet) GetAccountUtxos(accountID string, id string, unconfirmed, isSmartContract bool, vote bool) []*account.UTXO { accountUtxos := []*account.UTXO{} if unconfirmed { accountUtxos = w.AccountMgr.ListUnconfirmedUtxo(accountID, isSmartContract) } - accountUtxoIter := w.DB.IteratorPrefix([]byte(prefix + id)) - defer accountUtxoIter.Release() + confirmedUTXOs, err := w.Store.ListAccountUTXOs(id, isSmartContract) + if err != nil { + log.WithFields(log.Fields{"module": logModule, "err": err}).Error("GetAccountUtxos fail.") + } + accountUtxos = append(accountUtxos, confirmedUTXOs...) - for accountUtxoIter.Next() { - accountUtxo := &account.UTXO{} - if err := json.Unmarshal(accountUtxoIter.Value(), accountUtxo); err != nil { - log.WithField("err", err).Warn("GetAccountUtxos fail on unmarshal utxo") + newAccountUtxos := []*account.UTXO{} + for _, accountUtxo := range accountUtxos { + if vote && accountUtxo.Vote == nil { continue } - if accountID == accountUtxo.AccountID || accountID == "" { - accountUtxos = append(accountUtxos, accountUtxo) + newAccountUtxos = append(newAccountUtxos, accountUtxo) } } - return accountUtxos + return newAccountUtxos } -func (w *Wallet) attachUtxos(batch db.Batch, b *types.Block, txStatus *bc.TransactionStatus) { +func (w *Wallet) attachUtxos(b *types.Block, txStatus *bc.TransactionStatus, store WalletStore) { for txIndex, tx := range b.Transactions { statusFail, err := txStatus.GetStatus(txIndex) if err != nil { - log.WithField("err", err).Error("attachUtxos fail on get tx status") + log.WithFields(log.Fields{"module": logModule, "err": err}).Error("attachUtxos fail on get tx status") continue } @@ -56,51 +48,55 @@ func (w *Wallet) attachUtxos(batch db.Batch, b *types.Block, txStatus *bc.Transa inputUtxos := txInToUtxos(tx, statusFail) for _, inputUtxo := range inputUtxos { if segwit.IsP2WScript(inputUtxo.ControlProgram) { - batch.Delete(account.StandardUTXOKey(inputUtxo.OutputID)) + w.AccountMgr.DeleteStandardUTXO(inputUtxo.OutputID) } else { - batch.Delete(account.ContractUTXOKey(inputUtxo.OutputID)) + store.DeleteContractUTXO(inputUtxo.OutputID) } } //hand update the transaction output utxos - validHeight := uint64(0) - if txIndex == 0 { - validHeight = b.Height + consensus.CoinbasePendingBlockNumber - } - outputUtxos := txOutToUtxos(tx, statusFail, validHeight) + outputUtxos := txOutToUtxos(tx, statusFail, b.Height) utxos := w.filterAccountUtxo(outputUtxos) - if err := batchSaveUtxos(utxos, batch); err != nil { - log.WithField("err", err).Error("attachUtxos fail on batchSaveUtxos") + if err := w.saveUtxos(utxos, store); err != nil { + log.WithFields(log.Fields{"module": logModule, "err": err}).Error("attachUtxos fail on saveUtxos") } } } -func (w *Wallet) detachUtxos(batch db.Batch, b *types.Block, txStatus *bc.TransactionStatus) { +func (w *Wallet) detachUtxos(b *types.Block, txStatus *bc.TransactionStatus, store WalletStore) { for txIndex := len(b.Transactions) - 1; txIndex >= 0; txIndex-- { tx := b.Transactions[txIndex] for j := range tx.Outputs { - resOut, err := tx.Output(*tx.ResultIds[j]) - if err != nil { + code := []byte{} + switch resOut := tx.Entries[*tx.ResultIds[j]].(type) { + case *bc.IntraChainOutput: + if resOut.Source.Value.Amount == uint64(0) { + continue + } + code = resOut.ControlProgram.Code + case *bc.VoteOutput: + code = resOut.ControlProgram.Code + default: continue } - if segwit.IsP2WScript(resOut.ControlProgram.Code) { - batch.Delete(account.StandardUTXOKey(*tx.ResultIds[j])) + if segwit.IsP2WScript(code) { + w.AccountMgr.DeleteStandardUTXO(*tx.ResultIds[j]) } else { - batch.Delete(account.ContractUTXOKey(*tx.ResultIds[j])) + store.DeleteContractUTXO(*tx.ResultIds[j]) } } statusFail, err := txStatus.GetStatus(txIndex) if err != nil { - log.WithField("err", err).Error("detachUtxos fail on get tx status") + log.WithFields(log.Fields{"module": logModule, "err": err}).Error("detachUtxos fail on get tx status") continue } inputUtxos := txInToUtxos(tx, statusFail) utxos := w.filterAccountUtxo(inputUtxos) - if err := batchSaveUtxos(utxos, batch); err != nil { - log.WithField("err", err).Error("detachUtxos fail on batchSaveUtxos") + if err := w.saveUtxos(utxos, store); err != nil { + log.WithFields(log.Fields{"module": logModule, "err": err}).Error("detachUtxos fail on batchSaveUtxos") return } } @@ -124,14 +120,12 @@ func (w *Wallet) filterAccountUtxo(utxos []*account.UTXO) []*account.UTXO { var hash [32]byte sha3pool.Sum256(hash[:], []byte(s)) - data := w.DB.Get(account.ContractKey(hash)) - if data == nil { + cp, err := w.AccountMgr.GetControlProgram(bc.NewHash(hash)) + if err != nil && err != account.ErrFindCtrlProgram { + log.WithFields(log.Fields{"module": logModule, "err": err}).Error("filterAccountUtxo fail.") continue } - - cp := &account.CtrlProgram{} - if err := json.Unmarshal(data, cp); err != nil { - log.WithField("err", err).Error("filterAccountUtxo fail on unmarshal control program") + if cp == nil { continue } @@ -146,17 +140,16 @@ func (w *Wallet) filterAccountUtxo(utxos []*account.UTXO) []*account.UTXO { return result } -func batchSaveUtxos(utxos []*account.UTXO, batch db.Batch) error { +func (w *Wallet) saveUtxos(utxos []*account.UTXO, store WalletStore) error { for _, utxo := range utxos { - data, err := json.Marshal(utxo) - if err != nil { - return errors.Wrap(err, "failed marshal accountutxo") - } - if segwit.IsP2WScript(utxo.ControlProgram) { - batch.Set(account.StandardUTXOKey(utxo.OutputID), data) + if err := w.AccountMgr.SetStandardUTXO(utxo.OutputID, utxo); err != nil { + return err + } } else { - batch.Set(account.ContractUTXOKey(utxo.OutputID), data) + if err := store.SetContractUTXO(utxo.OutputID, utxo); err != nil { + return err + } } } return nil @@ -165,54 +158,113 @@ func batchSaveUtxos(utxos []*account.UTXO, batch db.Batch) error { func txInToUtxos(tx *types.Tx, statusFail bool) []*account.UTXO { utxos := []*account.UTXO{} for _, inpID := range tx.Tx.InputIDs { - sp, err := tx.Spend(inpID) - if err != nil { - continue - } - resOut, err := tx.Output(*sp.SpentOutputId) + e, err := tx.Entry(inpID) if err != nil { - log.WithField("err", err).Error("txInToUtxos fail on get resOut") continue } - - if statusFail && *resOut.Source.Value.AssetId != *consensus.BTMAssetID { + utxo := &account.UTXO{} + switch inp := e.(type) { + case *bc.Spend: + resOut, err := tx.IntraChainOutput(*inp.SpentOutputId) + if err != nil { + log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txInToUtxos fail on get resOut for spedn") + continue + } + if statusFail && *resOut.Source.Value.AssetId != *consensus.BTMAssetID { + continue + } + utxo = &account.UTXO{ + OutputID: *inp.SpentOutputId, + AssetID: *resOut.Source.Value.AssetId, + Amount: resOut.Source.Value.Amount, + ControlProgram: resOut.ControlProgram.Code, + SourceID: *resOut.Source.Ref, + SourcePos: resOut.Source.Position, + } + case *bc.VetoInput: + resOut, err := tx.VoteOutput(*inp.SpentOutputId) + if err != nil { + log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txInToUtxos fail on get resOut for vetoInput") + continue + } + if statusFail && *resOut.Source.Value.AssetId != *consensus.BTMAssetID { + continue + } + utxo = &account.UTXO{ + OutputID: *inp.SpentOutputId, + AssetID: *resOut.Source.Value.AssetId, + Amount: resOut.Source.Value.Amount, + ControlProgram: resOut.ControlProgram.Code, + SourceID: *resOut.Source.Ref, + SourcePos: resOut.Source.Position, + Vote: resOut.Vote, + } + default: continue } - - utxos = append(utxos, &account.UTXO{ - OutputID: *sp.SpentOutputId, - AssetID: *resOut.Source.Value.AssetId, - Amount: resOut.Source.Value.Amount, - ControlProgram: resOut.ControlProgram.Code, - SourceID: *resOut.Source.Ref, - SourcePos: resOut.Source.Position, - }) + utxos = append(utxos, utxo) } return utxos } -func txOutToUtxos(tx *types.Tx, statusFail bool, vaildHeight uint64) []*account.UTXO { +func txOutToUtxos(tx *types.Tx, statusFail bool, blockHeight uint64) []*account.UTXO { + validHeight := uint64(0) + if tx.Inputs[0].InputType() == types.CoinbaseInputType { + validHeight = blockHeight + consensus.ActiveNetParams.CoinbasePendingBlockNumber + } + utxos := []*account.UTXO{} for i, out := range tx.Outputs { - bcOut, err := tx.Output(*tx.ResultIds[i]) + entryOutput, err := tx.Entry(*tx.ResultIds[i]) if err != nil { + log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txOutToUtxos fail on get entryOutput") continue } - if statusFail && *out.AssetAmount.AssetId != *consensus.BTMAssetID { + utxo := &account.UTXO{} + switch bcOut := entryOutput.(type) { + case *bc.IntraChainOutput: + if (statusFail && *out.AssetAmount().AssetId != *consensus.BTMAssetID) || out.AssetAmount().Amount == uint64(0) { + continue + } + utxo = &account.UTXO{ + OutputID: *tx.OutputID(i), + AssetID: *out.AssetAmount().AssetId, + Amount: out.AssetAmount().Amount, + ControlProgram: out.ControlProgram(), + SourceID: *bcOut.Source.Ref, + SourcePos: bcOut.Source.Position, + ValidHeight: validHeight, + } + + case *bc.VoteOutput: + if statusFail && *out.AssetAmount().AssetId != *consensus.BTMAssetID { + continue + } + + voteValidHeight := blockHeight + consensus.ActiveNetParams.VotePendingBlockNumber + if validHeight < voteValidHeight { + validHeight = voteValidHeight + } + + utxo = &account.UTXO{ + OutputID: *tx.OutputID(i), + AssetID: *out.AssetAmount().AssetId, + Amount: out.AssetAmount().Amount, + ControlProgram: out.ControlProgram(), + SourceID: *bcOut.Source.Ref, + SourcePos: bcOut.Source.Position, + ValidHeight: validHeight, + Vote: bcOut.Vote, + } + + default: + log.WithFields(log.Fields{"module": logModule}).Warn("txOutToUtxos fail on get bcOut") continue } - utxos = append(utxos, &account.UTXO{ - OutputID: *tx.OutputID(i), - AssetID: *out.AssetAmount.AssetId, - Amount: out.Amount, - ControlProgram: out.ControlProgram, - SourceID: *bcOut.Source.Ref, - SourcePos: bcOut.Source.Position, - ValidHeight: vaildHeight, - }) + utxos = append(utxos, utxo) } return utxos }