You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: utxo_view.go
    path of file: ./repos/bytom/protocol/state
    
    the code of the file until where you have to start completion: package state

import (
	"github.com/bytom/bytom/consensus"
	"github.com/bytom/bytom/database/storage"
	"github.com/bytom/bytom/errors"
	"github.com/bytom/bytom/protocol/bc"
)

// UtxoViewpoint represents a view into the set of unspent transaction outputs
type UtxoViewpoint struct {
	Entries map[bc.Hash]*storage.UtxoEntry
}

// NewUtxoViewpoint returns a new empty unspent transaction output view.
func NewUtxoViewpoint() *UtxoViewpoint {
	return &UtxoViewpoint{
		Entries: make(map[bc.Hash]*storage.UtxoEntry),
	}
}

func (view *UtxoViewpoint) ApplyTransaction(block *bc.Block, tx *bc.Tx) error {
	if err := view.applySpendUtxo(block, tx); err != nil {
		return err
	}

	return view.applyOutputUtxo(block, tx)
}

func (v
    
func (view *UtxoViewpoint) detachSpendUtxo(tx *bc.Tx) error {
	for _, prevout := range tx.SpentOutputIDs {
		entryOutput, ok := tx.Entries[prevout]
		if !ok {
			return errors.New("fail to find utxo entry")
		}

		var utxoType uint32
		switch entryOutput.(type) {
		case *bc.OriginalOutput:
			utxoType = storage.NormalUTXOType
		case *bc.VoteOutput:
			utxoType = storage.VoteUTXOType
		default:
			return errors.Wrapf(bc.ErrEntryType, "entry %x has unexpected type %T", prevout.Bytes(), entryOutput)
		}

		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
}