OSDN Git Service

7380bf11483005e9e76a8a513c0c6e08b380c236
[bytom/vapor.git] / protocol / bc / types / txoutput.go
1 package types
2
3 import (
4         "fmt"
5         "io"
6
7         "github.com/vapor/encoding/blockchain"
8         "github.com/vapor/errors"
9         "github.com/vapor/protocol/bc"
10 )
11
12 // serflag variables for output types.
13 const (
14         IntraChainOutputType uint8 = iota
15         CrossChainOutputType
16         VoteOutputType
17 )
18
19 type (
20         // TxOutput is the top level struct of tx output.
21         TxOutput struct {
22                 AssetVersion uint64
23                 TypedOutput
24                 // Unconsumed suffixes of the commitment and witness extensible strings.
25                 CommitmentSuffix []byte
26         }
27
28         // TypedOutput return the txoutput type.
29         TypedOutput interface {
30                 OutputType() uint8
31         }
32 )
33
34 // OutputCommitment return the OutputCommitment of a txoutput.
35 func (to *TxOutput) OutputCommitment() OutputCommitment {
36         switch outp := to.TypedOutput.(type) {
37         case *IntraChainOutput:
38                 return outp.OutputCommitment
39
40         case *CrossChainOutput:
41                 return outp.OutputCommitment
42
43         case *VoteOutput:
44                 return outp.OutputCommitment
45
46         default:
47                 return OutputCommitment{}
48         }
49 }
50
51 // AssetAmount return the asset id and amount of a txoutput.
52 func (to *TxOutput) AssetAmount() bc.AssetAmount {
53         switch outp := to.TypedOutput.(type) {
54         case *IntraChainOutput:
55                 return outp.AssetAmount
56
57         case *CrossChainOutput:
58                 return outp.AssetAmount
59
60         case *VoteOutput:
61                 return outp.AssetAmount
62
63         default:
64                 return bc.AssetAmount{}
65         }
66 }
67
68 // ControlProgram return the control program of the txoutput
69 func (to *TxOutput) ControlProgram() []byte {
70         switch outp := to.TypedOutput.(type) {
71         case *IntraChainOutput:
72                 return outp.ControlProgram
73
74         case *CrossChainOutput:
75                 return outp.ControlProgram
76
77         case *VoteOutput:
78                 return outp.ControlProgram
79
80         default:
81                 return nil
82         }
83 }
84
85 // VMVersion return the VM version of the txoutput
86 func (to *TxOutput) VMVersion() uint64 {
87         switch outp := to.TypedOutput.(type) {
88         case *IntraChainOutput:
89                 return outp.VMVersion
90
91         case *CrossChainOutput:
92                 return outp.VMVersion
93
94         case *VoteOutput:
95                 return outp.VMVersion
96
97         default:
98                 return 0
99         }
100 }
101
102 func (to *TxOutput) readFrom(r *blockchain.Reader) (err error) {
103         if to.AssetVersion, err = blockchain.ReadVarint63(r); err != nil {
104                 return errors.Wrap(err, "reading asset version")
105         }
106
107         to.CommitmentSuffix, err = blockchain.ReadExtensibleString(r, func(r *blockchain.Reader) error {
108                 if to.AssetVersion != 1 {
109                         return nil
110                 }
111
112                 var outType [1]byte
113                 if _, err = io.ReadFull(r, outType[:]); err != nil {
114                         return errors.Wrap(err, "reading output type")
115                 }
116
117                 switch outType[0] {
118                 case IntraChainOutputType:
119                         out := new(IntraChainOutput)
120                         to.TypedOutput = out
121                         if out.CommitmentSuffix, err = out.OutputCommitment.readFrom(r, to.AssetVersion); err != nil {
122                                 return errors.Wrap(err, "reading intra-chain output commitment")
123                         }
124
125                 case CrossChainOutputType:
126                         out := new(CrossChainOutput)
127                         to.TypedOutput = out
128                         if out.CommitmentSuffix, err = out.OutputCommitment.readFrom(r, to.AssetVersion); err != nil {
129                                 return errors.Wrap(err, "reading cross-chain output commitment")
130                         }
131
132                 case VoteOutputType:
133                         out := new(VoteOutput)
134                         to.TypedOutput = out
135                         if out.Vote, err = blockchain.ReadVarstr31(r); err != nil {
136                                 return errors.Wrap(err, "reading vote output vote")
137                         }
138
139                         if out.CommitmentSuffix, err = out.OutputCommitment.readFrom(r, to.AssetVersion); err != nil {
140                                 return errors.Wrap(err, "reading vote output commitment")
141                         }
142
143                 default:
144                         return fmt.Errorf("unsupported output type %d", outType[0])
145                 }
146
147                 return nil
148         })
149
150         if err != nil {
151                 return err
152         }
153
154         // read and ignore the (empty) output witness
155         _, err = blockchain.ReadVarstr31(r)
156         return errors.Wrap(err, "reading output witness")
157 }
158
159 func (to *TxOutput) writeTo(w io.Writer) error {
160         if _, err := blockchain.WriteVarint63(w, to.AssetVersion); err != nil {
161                 return errors.Wrap(err, "writing asset version")
162         }
163
164         if _, err := blockchain.WriteExtensibleString(w, to.CommitmentSuffix, to.writeOutputCommitment); err != nil {
165                 return errors.Wrap(err, "writing output commitment")
166         }
167
168         if _, err := blockchain.WriteVarstr31(w, nil); err != nil {
169                 return errors.Wrap(err, "writing witness")
170         }
171
172         return nil
173 }
174
175 func (to *TxOutput) writeOutputCommitment(w io.Writer) error {
176         if to.AssetVersion != 1 {
177                 return nil
178         }
179
180         switch outp := to.TypedOutput.(type) {
181         case *IntraChainOutput:
182                 if _, err := w.Write([]byte{IntraChainOutputType}); err != nil {
183                         return err
184                 }
185                 return outp.OutputCommitment.writeExtensibleString(w, outp.CommitmentSuffix, to.AssetVersion)
186
187         case *CrossChainOutput:
188                 if _, err := w.Write([]byte{CrossChainOutputType}); err != nil {
189                         return err
190                 }
191                 return outp.OutputCommitment.writeExtensibleString(w, outp.CommitmentSuffix, to.AssetVersion)
192
193         case *VoteOutput:
194                 if _, err := w.Write([]byte{VoteOutputType}); err != nil {
195                         return err
196                 }
197                 if _, err := blockchain.WriteVarstr31(w, outp.Vote); err != nil {
198                         return err
199                 }
200                 return outp.OutputCommitment.writeExtensibleString(w, outp.CommitmentSuffix, to.AssetVersion)
201
202         default:
203                 return nil
204         }
205 }
206
207 // ComputeOutputID assembles an intra-chain(!) output entry given a spend
208 // commitment and computes and returns its corresponding entry ID.
209 func ComputeOutputID(sc *SpendCommitment, inputType uint8, vote []byte) (h bc.Hash, err error) {
210         defer func() {
211                 if r, ok := recover().(error); ok {
212                         err = r
213                 }
214         }()
215         src := &bc.ValueSource{
216                 Ref:      &sc.SourceID,
217                 Value:    &sc.AssetAmount,
218                 Position: sc.SourcePosition,
219         }
220         var o bc.Entry
221         switch inputType {
222         case SpendInputType:
223                 o = bc.NewIntraChainOutput(src, &bc.Program{VmVersion: sc.VMVersion, Code: sc.ControlProgram}, 0)
224         case VetoInputType:
225                 o = bc.NewVoteOutput(src, &bc.Program{VmVersion: sc.VMVersion, Code: sc.ControlProgram}, 0, vote)
226         default:
227                 return h, fmt.Errorf("Input type error:[%v]", inputType)
228         }
229
230         h = bc.EntryID(o)
231         return h, nil
232 }