github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/protocol/bc/types/txoutput.go (about)

     1  package types
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  
     7  	"github.com/bytom/bytom/encoding/blockchain"
     8  	"github.com/bytom/bytom/errors"
     9  	"github.com/bytom/bytom/protocol/bc"
    10  )
    11  
    12  const (
    13  	// OriginalOutputType represent the type of original output
    14  	OriginalOutputType uint8 = iota
    15  
    16  	// VoteOutputType represent the type of vote output
    17  	VoteOutputType
    18  )
    19  
    20  // TxOutput is the top level struct of tx output.
    21  type TxOutput struct {
    22  	AssetVersion uint64
    23  	OutputCommitment
    24  	// Unconsumed suffixes of the commitment and witness extensible strings.
    25  	CommitmentSuffix []byte
    26  	TypedOutput
    27  }
    28  
    29  // TypedOutput return the txoutput type.
    30  type TypedOutput interface {
    31  	OutputType() uint8
    32  	readFrom(*blockchain.Reader) error
    33  	writeTo(io.Writer) error
    34  }
    35  
    36  var outputTypeMap = map[uint8]func() TypedOutput{
    37  	OriginalOutputType: func() TypedOutput { return &originalTxOutput{} },
    38  	VoteOutputType:     func() TypedOutput { return &VoteOutput{} },
    39  }
    40  
    41  func parseTypedOutput(r *blockchain.Reader) (TypedOutput, error) {
    42  	var outType [1]byte
    43  	if _, err := io.ReadFull(r, outType[:]); err != nil {
    44  		return nil, errors.Wrap(err, "reading output type")
    45  	}
    46  
    47  	newOutFun, ok := outputTypeMap[outType[0]]
    48  	if !ok {
    49  		return nil, fmt.Errorf("unsupported output type %d", outType[0])
    50  	}
    51  
    52  	return newOutFun(), nil
    53  }
    54  
    55  func (to *TxOutput) readFrom(r *blockchain.Reader) (err error) {
    56  	if to.AssetVersion, err = blockchain.ReadVarint63(r); err != nil {
    57  		return errors.Wrap(err, "reading asset version")
    58  	}
    59  
    60  	to.TypedOutput, err = parseTypedOutput(r)
    61  	if err != nil {
    62  		return errors.Wrap(err, "parse typedOutput")
    63  	}
    64  
    65  	if to.CommitmentSuffix, err = blockchain.ReadExtensibleString(r, func(reader *blockchain.Reader) error {
    66  		if err := to.TypedOutput.readFrom(reader); err != nil {
    67  			return err
    68  		}
    69  
    70  		return to.OutputCommitment.readFrom(reader, to.AssetVersion)
    71  	}); err != nil {
    72  		return errors.Wrap(err, "reading output commitment")
    73  	}
    74  
    75  	// read and ignore the (empty) output witness
    76  	_, err = blockchain.ReadVarstr31(r)
    77  	return errors.Wrap(err, "reading output witness")
    78  }
    79  
    80  func (to *TxOutput) writeTo(w io.Writer) error {
    81  	if _, err := blockchain.WriteVarint63(w, to.AssetVersion); err != nil {
    82  		return errors.Wrap(err, "writing asset version")
    83  	}
    84  
    85  	if _, err := w.Write([]byte{to.OutputType()}); err != nil {
    86  		return err
    87  	}
    88  
    89  	if _, err := blockchain.WriteExtensibleString(w, to.CommitmentSuffix, func(writer io.Writer) error {
    90  		if err := to.TypedOutput.writeTo(writer); err != nil {
    91  			return err
    92  		}
    93  
    94  		return to.OutputCommitment.writeTo(writer, to.AssetVersion)
    95  	}); err != nil {
    96  		return errors.Wrap(err, "writing output commitment")
    97  	}
    98  
    99  	if _, err := blockchain.WriteVarstr31(w, nil); err != nil {
   100  		return errors.Wrap(err, "writing witness")
   101  	}
   102  	return nil
   103  }
   104  
   105  // ComputeOutputID assembles an output entry given a spend commitment and
   106  // computes and returns its corresponding entry ID.
   107  func ComputeOutputID(sc *SpendCommitment, inputType uint8, vote []byte) (h bc.Hash, err error) {
   108  	defer func() {
   109  		if r, ok := recover().(error); ok {
   110  			err = r
   111  		}
   112  	}()
   113  	src := &bc.ValueSource{
   114  		Ref:      &sc.SourceID,
   115  		Value:    &sc.AssetAmount,
   116  		Position: sc.SourcePosition,
   117  	}
   118  
   119  	var o bc.Entry
   120  	switch inputType {
   121  	case SpendInputType:
   122  		o = bc.NewOriginalOutput(src, &bc.Program{VmVersion: sc.VMVersion, Code: sc.ControlProgram}, sc.StateData, 0)
   123  	case VetoInputType:
   124  		o = bc.NewVoteOutput(src, &bc.Program{VmVersion: sc.VMVersion, Code: sc.ControlProgram}, sc.StateData, 0, vote)
   125  	default:
   126  		return h, fmt.Errorf("input type error:[%v]", inputType)
   127  	}
   128  
   129  	return bc.EntryID(o), nil
   130  }