OSDN Git Service

edit types (#1961)
[bytom/bytom.git] / protocol / bc / types / txoutput.go
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) (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         o := bc.NewOutput(src, &bc.Program{VmVersion: sc.VMVersion, Code: sc.ControlProgram}, &bc.StateData{StateData: sc.StateData}, 0)
120         return bc.EntryID(o), nil
121 }