OSDN Git Service

edit code while reviw (#253)
[bytom/vapor.git] / protocol / state / utxo_view.go
index 90c71b1..4e0d56c 100644 (file)
@@ -20,80 +20,93 @@ func NewUtxoViewpoint() *UtxoViewpoint {
 }
 
 func (view *UtxoViewpoint) ApplyTransaction(block *bc.Block, tx *bc.Tx, statusFail bool) error {
 }
 
 func (view *UtxoViewpoint) ApplyTransaction(block *bc.Block, tx *bc.Tx, statusFail bool) error {
-       for _, prevout := range tx.MainchainOutputIDs {
-               entry, ok := view.Entries[prevout]
-               if !ok {
-                       return errors.New("fail to find mainchain output entry")
-               }
+       if err := view.applyCrossChainUtxo(block, tx); err != nil {
+               return err
+       }
 
 
-               if entry.Type != storage.CrosschainUTXOType {
-                       return errors.New("look up mainchainOutputID but find utxo not from mainchain")
+       if err := view.applySpendUtxo(block, tx, statusFail); err != nil {
+               return err
+       }
+
+       return view.applyOutputUtxo(block, tx, statusFail)
+}
+
+func (view *UtxoViewpoint) ApplyBlock(block *bc.Block, txStatus *bc.TransactionStatus) error {
+       for i, tx := range block.Transactions {
+               statusFail, err := txStatus.GetStatus(i)
+               if err != nil {
+                       return err
                }
 
                }
 
-               if entry.Spent {
-                       return errors.New("mainchain output has been spent")
+               if err := view.ApplyTransaction(block, tx, statusFail); err != nil {
+                       return err
                }
                }
+       }
+       return nil
+}
 
 
-               entry.BlockHeight = block.Height
-               entry.SpendOutput()
+func (view *UtxoViewpoint) CanSpend(hash *bc.Hash) bool {
+       entry := view.Entries[*hash]
+       return entry != nil && !entry.Spent
+}
+
+func (view *UtxoViewpoint) DetachTransaction(tx *bc.Tx, statusFail bool) error {
+       if err := view.detachCrossChainUtxo(tx); err != nil {
+               return err
        }
 
        }
 
-       for _, prevout := range tx.SpentOutputIDs {
-               assetID := bc.AssetID{}
-               entryOutput, err := tx.Entry(prevout)
+       if err := view.detachSpendUtxo(tx, statusFail); err != nil {
+               return err
+       }
+
+       return view.detachOutputUtxo(tx, statusFail)
+}
+
+func (view *UtxoViewpoint) DetachBlock(block *bc.Block, txStatus *bc.TransactionStatus) error {
+       for i := len(block.Transactions) - 1; i >= 0; i-- {
+               statusFail, err := txStatus.GetStatus(i)
                if err != nil {
                        return err
                }
 
                if err != nil {
                        return err
                }
 
-               switch output := entryOutput.(type) {
-               case *bc.IntraChainOutput:
-                       assetID = *output.Source.Value.AssetId
-               case *bc.VoteOutput:
-                       assetID = *output.Source.Value.AssetId
-               default:
-                       return errors.Wrapf(bc.ErrEntryType, "entry %x has unexpected type %T", prevout.Bytes(), entryOutput)
+               if err := view.DetachTransaction(block.Transactions[i], statusFail); err != nil {
+                       return err
                }
                }
+       }
+       return nil
+}
 
 
-               if statusFail && assetID != *consensus.BTMAssetID {
-                       continue
-               }
+func (view *UtxoViewpoint) HasUtxo(hash *bc.Hash) bool {
+       _, ok := view.Entries[*hash]
+       return ok
+}
 
 
+func (view *UtxoViewpoint) applyCrossChainUtxo(block *bc.Block, tx *bc.Tx) error {
+       for _, prevout := range tx.MainchainOutputIDs {
                entry, ok := view.Entries[prevout]
                if !ok {
                entry, ok := view.Entries[prevout]
                if !ok {
-                       return errors.New("fail to find utxo entry")
+                       return errors.New("fail to find mainchain output entry")
                }
 
                if entry.Spent {
                }
 
                if entry.Spent {
-                       return errors.New("utxo has been spent")
-               }
-
-               switch entry.Type {
-               case storage.CrosschainUTXOType:
-                       return errors.New("look up spentOutputID but find utxo from mainchain")
-
-               case storage.CoinbaseUTXOType:
-                       if (entry.BlockHeight + consensus.CoinbasePendingBlockNumber) > block.Height {
-                               return errors.New("coinbase utxo is not ready for use")
-                       }
-
-               case storage.VoteUTXOType:
-                       if (entry.BlockHeight + consensus.VotePendingBlockNumber) > block.Height {
-                               return errors.New("Coin is  within the voting lock time")
-                       }
+                       return errors.New("mainchain output has been spent")
                }
 
                }
 
+               entry.BlockHeight = block.Height
                entry.SpendOutput()
        }
                entry.SpendOutput()
        }
+       return nil
+}
 
 
+func (view *UtxoViewpoint) applyOutputUtxo(block *bc.Block, tx *bc.Tx, statusFail bool) error {
        for _, id := range tx.TxHeader.ResultIds {
        for _, id := range tx.TxHeader.ResultIds {
-               assetID := bc.AssetID{}
                entryOutput, err := tx.Entry(*id)
                if err != nil {
                entryOutput, err := tx.Entry(*id)
                if err != nil {
-                       continue
+                       return err
                }
 
                }
 
+               var assetID bc.AssetID
                utxoType := storage.NormalUTXOType
                utxoType := storage.NormalUTXOType
-
                switch output := entryOutput.(type) {
                case *bc.IntraChainOutput:
                        if output.Source.Value.Amount == uint64(0) {
                switch output := entryOutput.(type) {
                case *bc.IntraChainOutput:
                        if output.Source.Value.Amount == uint64(0) {
@@ -120,57 +133,19 @@ func (view *UtxoViewpoint) ApplyTransaction(block *bc.Block, tx *bc.Tx, statusFa
        return nil
 }
 
        return nil
 }
 
-func (view *UtxoViewpoint) ApplyBlock(block *bc.Block, txStatus *bc.TransactionStatus) error {
-       for i, tx := range block.Transactions {
-               statusFail, err := txStatus.GetStatus(i)
-               if err != nil {
-                       return err
-               }
-               if err := view.ApplyTransaction(block, tx, statusFail); err != nil {
-                       return err
-               }
-       }
-       return nil
-}
-
-func (view *UtxoViewpoint) CanSpend(hash *bc.Hash) bool {
-       entry := view.Entries[*hash]
-       return entry != nil && !entry.Spent
-}
-
-func (view *UtxoViewpoint) DetachTransaction(tx *bc.Tx, statusFail bool) error {
-       for _, prevout := range tx.MainchainOutputIDs {
-               // don't simply delete(view.Entries, prevout), because we need to delete from db in saveUtxoView()
-               entry, ok := view.Entries[prevout]
-               if ok && (entry.Type != storage.CrosschainUTXOType) {
-                       return errors.New("look up mainchainOutputID but find utxo not from mainchain")
-               }
-
-               if ok && !entry.Spent {
-                       return errors.New("try to revert an unspent utxo")
-               }
-
-               if !ok {
-                       view.Entries[prevout] = storage.NewUtxoEntry(storage.CrosschainUTXOType, 0, false)
-                       continue
-               }
-               entry.UnspendOutput()
-       }
-
+func (view *UtxoViewpoint) applySpendUtxo(block *bc.Block, tx *bc.Tx, statusFail bool) error {
        for _, prevout := range tx.SpentOutputIDs {
        for _, prevout := range tx.SpentOutputIDs {
-               assetID := bc.AssetID{}
                entryOutput, err := tx.Entry(prevout)
                if err != nil {
                        return err
                }
 
                entryOutput, err := tx.Entry(prevout)
                if err != nil {
                        return err
                }
 
-               utxoType := storage.NormalUTXOType
+               var assetID bc.AssetID
                switch output := entryOutput.(type) {
                case *bc.IntraChainOutput:
                        assetID = *output.Source.Value.AssetId
                case *bc.VoteOutput:
                        assetID = *output.Source.Value.AssetId
                switch output := entryOutput.(type) {
                case *bc.IntraChainOutput:
                        assetID = *output.Source.Value.AssetId
                case *bc.VoteOutput:
                        assetID = *output.Source.Value.AssetId
-                       utxoType = storage.VoteUTXOType
                default:
                        return errors.Wrapf(bc.ErrEntryType, "entry %x has unexpected type %T", prevout.Bytes(), entryOutput)
                }
                default:
                        return errors.Wrapf(bc.ErrEntryType, "entry %x has unexpected type %T", prevout.Bytes(), entryOutput)
                }
@@ -180,28 +155,55 @@ func (view *UtxoViewpoint) DetachTransaction(tx *bc.Tx, statusFail bool) error {
                }
 
                entry, ok := view.Entries[prevout]
                }
 
                entry, ok := view.Entries[prevout]
-               if ok && (entry.Type == storage.CrosschainUTXOType) {
-                       return errors.New("look up SpentOutputIDs but find mainchain utxo")
+               if !ok {
+                       return errors.New("fail to find utxo entry")
                }
 
                }
 
-               if ok && !entry.Spent {
-                       return errors.New("try to revert an unspent utxo")
+               if entry.Spent {
+                       return errors.New("utxo has been spent")
                }
 
                }
 
+               switch entry.Type {
+               case storage.CoinbaseUTXOType:
+                       if (entry.BlockHeight + consensus.CoinbasePendingBlockNumber) > block.Height {
+                               return errors.New("coinbase utxo is not ready for use")
+                       }
+
+               case storage.VoteUTXOType:
+                       if (entry.BlockHeight + consensus.VotePendingBlockNumber) > block.Height {
+                               return errors.New("Coin is  within the voting lock time")
+                       }
+               }
+
+               entry.SpendOutput()
+       }
+       return nil
+}
+
+func (view *UtxoViewpoint) detachCrossChainUtxo(tx *bc.Tx) error {
+       for _, prevout := range tx.MainchainOutputIDs {
+               entry, ok := view.Entries[prevout]
                if !ok {
                if !ok {
-                       view.Entries[prevout] = storage.NewUtxoEntry(utxoType, 0, false)
-                       continue
+                       return errors.New("fail to find mainchain output entry")
                }
                }
+
+               if !entry.Spent {
+                       return errors.New("mainchain output is unspent")
+               }
+
                entry.UnspendOutput()
        }
                entry.UnspendOutput()
        }
+       return nil
+}
 
 
+func (view *UtxoViewpoint) detachOutputUtxo(tx *bc.Tx, statusFail bool) error {
        for _, id := range tx.TxHeader.ResultIds {
        for _, id := range tx.TxHeader.ResultIds {
-               assetID := bc.AssetID{}
                entryOutput, err := tx.Entry(*id)
                if err != nil {
                entryOutput, err := tx.Entry(*id)
                if err != nil {
-                       continue
+                       return err
                }
 
                }
 
+               var assetID bc.AssetID
                utxoType := storage.NormalUTXOType
                switch output := entryOutput.(type) {
                case *bc.IntraChainOutput:
                utxoType := storage.NormalUTXOType
                switch output := entryOutput.(type) {
                case *bc.IntraChainOutput:
@@ -226,20 +228,39 @@ func (view *UtxoViewpoint) DetachTransaction(tx *bc.Tx, statusFail bool) error {
        return nil
 }
 
        return nil
 }
 
-func (view *UtxoViewpoint) DetachBlock(block *bc.Block, txStatus *bc.TransactionStatus) error {
-       for i := len(block.Transactions) - 1; i >= 0; i-- {
-               statusFail, err := txStatus.GetStatus(i)
+func (view *UtxoViewpoint) detachSpendUtxo(tx *bc.Tx, statusFail bool) error {
+       for _, prevout := range tx.SpentOutputIDs {
+               entryOutput, err := tx.Entry(prevout)
                if err != nil {
                        return err
                }
                if err != nil {
                        return err
                }
-               if err := view.DetachTransaction(block.Transactions[i], statusFail); err != nil {
-                       return err
+
+               var assetID bc.AssetID
+               utxoType := storage.NormalUTXOType
+               switch output := entryOutput.(type) {
+               case *bc.IntraChainOutput:
+                       assetID = *output.Source.Value.AssetId
+               case *bc.VoteOutput:
+                       assetID = *output.Source.Value.AssetId
+                       utxoType = storage.VoteUTXOType
+               default:
+                       return errors.Wrapf(bc.ErrEntryType, "entry %x has unexpected type %T", prevout.Bytes(), entryOutput)
+               }
+
+               if statusFail && assetID != *consensus.BTMAssetID {
+                       continue
+               }
+
+               entry, ok := view.Entries[prevout]
+               if ok && !entry.Spent {
+                       return errors.New("try to revert an unspent utxo")
                }
                }
+
+               if !ok {
+                       view.Entries[prevout] = storage.NewUtxoEntry(utxoType, 0, false)
+                       continue
+               }
+               entry.UnspendOutput()
        }
        return nil
 }
        }
        return nil
 }
-
-func (view *UtxoViewpoint) HasUtxo(hash *bc.Hash) bool {
-       _, ok := view.Entries[*hash]
-       return ok
-}