OSDN Git Service

d1eeb57c679c51a255fe44eb3ff6239e673a91e9
[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
139         return o, err
140 }
141
142 func (t *TxInput) readFrom(r *blockchain.Reader) (err error) {
143         if t.AssetVersion, err = blockchain.ReadVarint63(r); err != nil {
144                 return err
145         }
146
147         t.CommitmentSuffix, err = blockchain.ReadExtensibleString(r, func(r *blockchain.Reader) error {
148                 if t.AssetVersion != 1 {
149                         return nil
150                 }
151                 var icType [1]byte
152                 if _, err = io.ReadFull(r, icType[:]); err != nil {
153                         return errors.Wrap(err, "reading input commitment type")
154                 }
155
156                 switch icType[0] {
157                 case SpendInputType:
158                         si := new(SpendInput)
159                         t.TypedInput = si
160                         if si.SpendCommitmentSuffix, err = si.SpendCommitment.readFrom(r, 1); err != nil {
161                                 return err
162                         }
163
164                 case CoinbaseInputType:
165                         ci := new(CoinbaseInput)
166                         t.TypedInput = ci
167                         if ci.Arbitrary, err = blockchain.ReadVarstr31(r); err != nil {
168                                 return err
169                         }
170
171                 case CrossChainInputType:
172                         ci := new(CrossChainInput)
173                         t.TypedInput = ci
174                         if ci.SpendCommitmentSuffix, err = ci.SpendCommitment.readFrom(r, 1); err != nil {
175                                 return err
176                         }
177
178                         if ci.IssuanceVMVersion, err = blockchain.ReadVarint63(r); err != nil {
179                                 return err
180                         }
181
182                         if ci.AssetDefinition, err = blockchain.ReadVarstr31(r); err != nil {
183                                 return err
184                         }
185
186                         if ci.IssuanceProgram, err = blockchain.ReadVarstr31(r); err != nil {
187                                 return err
188                         }
189
190                 case VetoInputType:
191                         ui := new(VetoInput)
192                         t.TypedInput = ui
193                         if ui.VetoCommitmentSuffix, err = ui.SpendCommitment.readFrom(r, 1); err != nil {
194                                 return err
195                         }
196
197                         if ui.Vote, err = blockchain.ReadVarstr31(r); err != nil {
198                                 return err
199                         }
200
201                 default:
202                         return fmt.Errorf("unsupported input type %d", icType[0])
203                 }
204                 return nil
205         })
206         if err != nil {
207                 return err
208         }
209
210         t.WitnessSuffix, err = blockchain.ReadExtensibleString(r, func(r *blockchain.Reader) error {
211                 if t.AssetVersion != 1 {
212                         return nil
213                 }
214
215                 var err error
216                 switch inp := t.TypedInput.(type) {
217                 case *SpendInput:
218                         inp.Arguments, err = blockchain.ReadVarstrList(r)
219
220                 case *CrossChainInput:
221                         inp.Arguments, err = blockchain.ReadVarstrList(r)
222
223                 case *VetoInput:
224                         inp.Arguments, err = blockchain.ReadVarstrList(r)
225                 }
226
227                 return err
228         })
229         return err
230 }
231
232 func (t *TxInput) writeTo(w io.Writer) error {
233         if _, err := blockchain.WriteVarint63(w, t.AssetVersion); err != nil {
234                 return errors.Wrap(err, "writing asset version")
235         }
236
237         if _, err := blockchain.WriteExtensibleString(w, t.CommitmentSuffix, t.writeInputCommitment); err != nil {
238                 return errors.Wrap(err, "writing input commitment")
239         }
240
241         if _, err := blockchain.WriteExtensibleString(w, t.WitnessSuffix, t.writeInputWitness); err != nil {
242                 return errors.Wrap(err, "writing input witness")
243         }
244
245         return nil
246 }
247
248 func (t *TxInput) writeInputCommitment(w io.Writer) (err error) {
249         if t.AssetVersion != 1 {
250                 return nil
251         }
252
253         switch inp := t.TypedInput.(type) {
254         case *SpendInput:
255                 if _, err = w.Write([]byte{SpendInputType}); err != nil {
256                         return err
257                 }
258
259                 return inp.SpendCommitment.writeExtensibleString(w, inp.SpendCommitmentSuffix, t.AssetVersion)
260
261         case *CrossChainInput:
262                 if _, err = w.Write([]byte{CrossChainInputType}); err != nil {
263                         return err
264                 }
265
266                 if err := inp.SpendCommitment.writeExtensibleString(w, inp.SpendCommitmentSuffix, t.AssetVersion); err != nil {
267                         return err
268                 }
269
270                 if _, err := blockchain.WriteVarint63(w, inp.IssuanceVMVersion); err != nil {
271                         return err
272                 }
273
274                 if _, err := blockchain.WriteVarstr31(w, inp.AssetDefinition); err != nil {
275                         return err
276                 }
277
278                 if _, err := blockchain.WriteVarstr31(w, inp.IssuanceProgram); err != nil {
279                         return err
280                 }
281
282         case *CoinbaseInput:
283                 if _, err := w.Write([]byte{CoinbaseInputType}); err != nil {
284                         return err
285                 }
286
287                 if _, err := blockchain.WriteVarstr31(w, inp.Arbitrary); err != nil {
288                         return errors.Wrap(err, "writing coinbase arbitrary")
289                 }
290
291         case *VetoInput:
292                 if _, err = w.Write([]byte{VetoInputType}); err != nil {
293                         return err
294                 }
295
296                 if err := inp.SpendCommitment.writeExtensibleString(w, inp.VetoCommitmentSuffix, t.AssetVersion); err != nil {
297                         return err
298                 }
299
300                 _, err := blockchain.WriteVarstr31(w, inp.Vote)
301                 return err
302         }
303         return nil
304 }
305
306 func (t *TxInput) writeInputWitness(w io.Writer) error {
307         if t.AssetVersion != 1 {
308                 return nil
309         }
310
311         var err error
312         switch inp := t.TypedInput.(type) {
313         case *SpendInput:
314                 _, err = blockchain.WriteVarstrList(w, inp.Arguments)
315
316         case *CrossChainInput:
317                 _, err = blockchain.WriteVarstrList(w, inp.Arguments)
318
319         case *VetoInput:
320                 _, err = blockchain.WriteVarstrList(w, inp.Arguments)
321         }
322         return err
323 }