OSDN Git Service

0d8402c36db7b786faf75cdb0ac8b525846abba6
[bytom/vapor.git] / protocol / bc / types / txinput.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 input types.
13 const (
14         CrossChainInputType uint8 = iota
15         SpendInputType
16         CoinbaseInputType
17         VetoInputType
18 )
19
20 type (
21         // TxInput is the top level struct of tx input.
22         TxInput struct {
23                 AssetVersion uint64
24                 TypedInput
25                 CommitmentSuffix []byte
26                 WitnessSuffix    []byte
27         }
28
29         // TypedInput return the txinput type.
30         TypedInput interface {
31                 InputType() uint8
32         }
33 )
34
35 // AssetAmount return the asset id and amount of the txinput.
36 func (t *TxInput) AssetAmount() bc.AssetAmount {
37         switch inp := t.TypedInput.(type) {
38         case *SpendInput:
39                 return inp.AssetAmount
40
41         case *CrossChainInput:
42                 return inp.AssetAmount
43
44         case *VetoInput:
45                 return inp.AssetAmount
46         }
47
48         return bc.AssetAmount{}
49 }
50
51 // AssetID return the assetID of the txinput
52 func (t *TxInput) AssetID() bc.AssetID {
53         switch inp := t.TypedInput.(type) {
54         case *SpendInput:
55                 return *inp.AssetId
56
57         case *CrossChainInput:
58                 return *inp.AssetAmount.AssetId
59
60         case *VetoInput:
61                 return *inp.AssetId
62
63         }
64         return bc.AssetID{}
65 }
66
67 // Amount return the asset amount of the txinput
68 func (t *TxInput) Amount() uint64 {
69         switch inp := t.TypedInput.(type) {
70         case *SpendInput:
71                 return inp.Amount
72
73         case *CrossChainInput:
74                 return inp.AssetAmount.Amount
75
76         case *VetoInput:
77                 return inp.Amount
78
79         }
80         return 0
81 }
82
83 // ControlProgram return the control program of the spend input
84 func (t *TxInput) ControlProgram() []byte {
85         switch inp := t.TypedInput.(type) {
86         case *SpendInput:
87                 return inp.ControlProgram
88
89         case *CrossChainInput:
90                 return inp.ControlProgram
91
92         case *VetoInput:
93                 return inp.ControlProgram
94
95         }
96
97         return nil
98 }
99
100 // Arguments get the args for the input
101 func (t *TxInput) Arguments() [][]byte {
102         switch inp := t.TypedInput.(type) {
103         case *SpendInput:
104                 return inp.Arguments
105
106         case *CrossChainInput:
107                 return inp.Arguments
108
109         case *VetoInput:
110                 return inp.Arguments
111         }
112         return nil
113 }
114
115 // SetArguments set the args for the input
116 func (t *TxInput) SetArguments(args [][]byte) {
117         switch inp := t.TypedInput.(type) {
118         case *SpendInput:
119                 inp.Arguments = args
120
121         case *CrossChainInput:
122                 inp.Arguments = args
123
124         case *VetoInput:
125                 inp.Arguments = args
126         }
127 }
128
129 // SpentOutputID calculate the hash of spended output
130 func (t *TxInput) SpentOutputID() (o bc.Hash, err error) {
131         switch inp := t.TypedInput.(type) {
132         case *SpendInput:
133                 o, err = ComputeOutputID(&inp.SpendCommitment, SpendInputType, nil)
134
135         case *VetoInput:
136                 o, err = ComputeOutputID(&inp.SpendCommitment, VetoInputType, inp.Vote)
137
138         case *CrossChainInput:
139                 o, err = ComputeOutputID(&inp.SpendCommitment, SpendInputType, nil)
140         }
141
142         return o, err
143 }
144
145 func (t *TxInput) readFrom(r *blockchain.Reader) (err error) {
146         if t.AssetVersion, err = blockchain.ReadVarint63(r); err != nil {
147                 return err
148         }
149
150         t.CommitmentSuffix, err = blockchain.ReadExtensibleString(r, func(r *blockchain.Reader) error {
151                 if t.AssetVersion != 1 {
152                         return nil
153                 }
154                 var icType [1]byte
155                 if _, err = io.ReadFull(r, icType[:]); err != nil {
156                         return errors.Wrap(err, "reading input commitment type")
157                 }
158
159                 switch icType[0] {
160                 case SpendInputType:
161                         si := new(SpendInput)
162                         t.TypedInput = si
163                         if si.SpendCommitmentSuffix, err = si.SpendCommitment.readFrom(r, 1); err != nil {
164                                 return err
165                         }
166
167                 case CoinbaseInputType:
168                         ci := new(CoinbaseInput)
169                         t.TypedInput = ci
170                         if ci.Arbitrary, err = blockchain.ReadVarstr31(r); err != nil {
171                                 return err
172                         }
173
174                 case CrossChainInputType:
175                         ci := new(CrossChainInput)
176                         t.TypedInput = ci
177                         if ci.SpendCommitmentSuffix, err = ci.SpendCommitment.readFrom(r, 1); err != nil {
178                                 return err
179                         }
180
181                         if ci.IssuanceVMVersion, err = blockchain.ReadVarint63(r); err != nil {
182                                 return err
183                         }
184
185                         if ci.AssetDefinition, err = blockchain.ReadVarstr31(r); err != nil {
186                                 return err
187                         }
188
189                         if ci.IssuanceProgram, err = blockchain.ReadVarstr31(r); err != nil {
190                                 return err
191                         }
192
193                 case VetoInputType:
194                         ui := new(VetoInput)
195                         t.TypedInput = ui
196                         if ui.VetoCommitmentSuffix, err = ui.SpendCommitment.readFrom(r, 1); err != nil {
197                                 return err
198                         }
199
200                         if ui.Vote, err = blockchain.ReadVarstr31(r); err != nil {
201                                 return err
202                         }
203
204                 default:
205                         return fmt.Errorf("unsupported input type %d", icType[0])
206                 }
207                 return nil
208         })
209         if err != nil {
210                 return err
211         }
212
213         t.WitnessSuffix, err = blockchain.ReadExtensibleString(r, func(r *blockchain.Reader) error {
214                 if t.AssetVersion != 1 {
215                         return nil
216                 }
217
218                 var err error
219                 switch inp := t.TypedInput.(type) {
220                 case *SpendInput:
221                         inp.Arguments, err = blockchain.ReadVarstrList(r)
222
223                 case *CrossChainInput:
224                         inp.Arguments, err = blockchain.ReadVarstrList(r)
225
226                 case *VetoInput:
227                         inp.Arguments, err = blockchain.ReadVarstrList(r)
228                 }
229
230                 return err
231         })
232         return err
233 }
234
235 func (t *TxInput) writeTo(w io.Writer) error {
236         if _, err := blockchain.WriteVarint63(w, t.AssetVersion); err != nil {
237                 return errors.Wrap(err, "writing asset version")
238         }
239
240         if _, err := blockchain.WriteExtensibleString(w, t.CommitmentSuffix, t.writeInputCommitment); err != nil {
241                 return errors.Wrap(err, "writing input commitment")
242         }
243
244         if _, err := blockchain.WriteExtensibleString(w, t.WitnessSuffix, t.writeInputWitness); err != nil {
245                 return errors.Wrap(err, "writing input witness")
246         }
247
248         return nil
249 }
250
251 func (t *TxInput) writeInputCommitment(w io.Writer) (err error) {
252         if t.AssetVersion != 1 {
253                 return nil
254         }
255
256         switch inp := t.TypedInput.(type) {
257         case *SpendInput:
258                 if _, err = w.Write([]byte{SpendInputType}); err != nil {
259                         return err
260                 }
261
262                 return inp.SpendCommitment.writeExtensibleString(w, inp.SpendCommitmentSuffix, t.AssetVersion)
263
264         case *CrossChainInput:
265                 if _, err = w.Write([]byte{CrossChainInputType}); err != nil {
266                         return err
267                 }
268
269                 if err := inp.SpendCommitment.writeExtensibleString(w, inp.SpendCommitmentSuffix, t.AssetVersion); err != nil {
270                         return err
271                 }
272
273                 if _, err := blockchain.WriteVarint63(w, inp.IssuanceVMVersion); err != nil {
274                         return err
275                 }
276
277                 if _, err := blockchain.WriteVarstr31(w, inp.AssetDefinition); err != nil {
278                         return err
279                 }
280
281                 if _, err := blockchain.WriteVarstr31(w, inp.IssuanceProgram); err != nil {
282                         return err
283                 }
284
285         case *CoinbaseInput:
286                 if _, err := w.Write([]byte{CoinbaseInputType}); err != nil {
287                         return err
288                 }
289
290                 if _, err := blockchain.WriteVarstr31(w, inp.Arbitrary); err != nil {
291                         return errors.Wrap(err, "writing coinbase arbitrary")
292                 }
293
294         case *VetoInput:
295                 if _, err = w.Write([]byte{VetoInputType}); err != nil {
296                         return err
297                 }
298
299                 if err := inp.SpendCommitment.writeExtensibleString(w, inp.VetoCommitmentSuffix, t.AssetVersion); err != nil {
300                         return err
301                 }
302
303                 _, err := blockchain.WriteVarstr31(w, inp.Vote)
304                 return err
305         }
306         return nil
307 }
308
309 func (t *TxInput) writeInputWitness(w io.Writer) error {
310         if t.AssetVersion != 1 {
311                 return nil
312         }
313
314         var err error
315         switch inp := t.TypedInput.(type) {
316         case *SpendInput:
317                 _, err = blockchain.WriteVarstrList(w, inp.Arguments)
318
319         case *CrossChainInput:
320                 _, err = blockchain.WriteVarstrList(w, inp.Arguments)
321
322         case *VetoInput:
323                 _, err = blockchain.WriteVarstrList(w, inp.Arguments)
324         }
325         return err
326 }