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 }