OSDN Git Service

58d01d63374325c2804b12c388aae44cd69be25d
[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         IssuanceInputType uint8 = iota
15         SpendInputType
16         CoinbaseInputType
17         ClainPeginInputType
18         DposInputType
19 )
20
21 type (
22         // TxInput is the top level struct of tx input.
23         TxInput struct {
24                 AssetVersion uint64
25                 TypedInput
26                 CommitmentSuffix []byte
27                 WitnessSuffix    []byte
28                 Peginwitness     [][]byte
29         }
30
31         // TypedInput return the txinput type.
32         TypedInput interface {
33                 InputType() uint8
34         }
35 )
36
37 var errBadAssetID = errors.New("asset ID does not match other issuance parameters")
38
39 // AssetAmount return the asset id and amount of the txinput.
40 func (t *TxInput) AssetAmount() bc.AssetAmount {
41         switch inp := t.TypedInput.(type) {
42         case *IssuanceInput:
43                 assetID := inp.AssetID()
44                 return bc.AssetAmount{
45                         AssetId: &assetID,
46                         Amount:  inp.Amount,
47                 }
48         case *SpendInput:
49                 return inp.AssetAmount
50         case *ClaimInput:
51                 return inp.AssetAmount
52         case *DposTx:
53                 return inp.AssetAmount
54         }
55         return bc.AssetAmount{}
56 }
57
58 // AssetID return the assetID of the txinput
59 func (t *TxInput) AssetID() bc.AssetID {
60         switch inp := t.TypedInput.(type) {
61         case *IssuanceInput:
62                 return inp.AssetID()
63         case *SpendInput:
64                 return *inp.AssetId
65         case *ClaimInput:
66                 return *inp.AssetId
67         case *DposTx:
68                 return *inp.AssetId
69
70         }
71         return bc.AssetID{}
72 }
73
74 // Amount return the asset amount of the txinput
75 func (t *TxInput) Amount() uint64 {
76         switch inp := t.TypedInput.(type) {
77         case *IssuanceInput:
78                 return inp.Amount
79         case *SpendInput:
80                 return inp.Amount
81         case *ClaimInput:
82                 return inp.Amount
83         case *DposTx:
84                 return inp.Amount
85         }
86         return 0
87 }
88
89 // ControlProgram return the control program of the spend input
90 func (t *TxInput) ControlProgram() []byte {
91         if si, ok := t.TypedInput.(*SpendInput); ok {
92                 return si.ControlProgram
93         }
94         return nil
95 }
96
97 // IssuanceProgram return the control program of the issuance input
98 func (t *TxInput) IssuanceProgram() []byte {
99         if ii, ok := t.TypedInput.(*IssuanceInput); ok {
100                 return ii.IssuanceProgram
101         }
102         return nil
103 }
104
105 // AssetDefinition return the asset definition of the issuance input
106 func (t *TxInput) AssetDefinition() []byte {
107         if ii, ok := t.TypedInput.(*IssuanceInput); ok {
108                 return ii.AssetDefinition
109         }
110         return nil
111 }
112
113 // Arguments get the args for the input
114 func (t *TxInput) Arguments() [][]byte {
115         switch inp := t.TypedInput.(type) {
116         case *IssuanceInput:
117                 return inp.Arguments
118         case *SpendInput:
119                 return inp.Arguments
120         case *ClaimInput:
121                 return inp.Arguments
122         case *DposTx:
123                 return inp.Arguments
124         }
125         return nil
126 }
127
128 // SetArguments set the args for the input
129 func (t *TxInput) SetArguments(args [][]byte) {
130         switch inp := t.TypedInput.(type) {
131         case *IssuanceInput:
132                 inp.Arguments = args
133         case *SpendInput:
134                 inp.Arguments = args
135         case *ClaimInput:
136                 inp.Arguments = args
137         case *DposTx:
138                 inp.Arguments = args
139         }
140 }
141
142 // SpentOutputID calculate the hash of spended output
143 func (t *TxInput) SpentOutputID() (o bc.Hash, err error) {
144         if si, ok := t.TypedInput.(*SpendInput); ok {
145                 o, err = ComputeOutputID(&si.SpendCommitment)
146         }
147         return o, err
148 }
149
150 func (t *TxInput) readFrom(r *blockchain.Reader) (err error) {
151         if t.AssetVersion, err = blockchain.ReadVarint63(r); err != nil {
152                 return err
153         }
154
155         if t.Peginwitness, err = blockchain.ReadVarstrList(r); err != nil {
156                 return err
157         }
158         var assetID bc.AssetID
159         t.CommitmentSuffix, err = blockchain.ReadExtensibleString(r, func(r *blockchain.Reader) error {
160                 if t.AssetVersion != 1 {
161                         return nil
162                 }
163                 var icType [1]byte
164                 if _, err = io.ReadFull(r, icType[:]); err != nil {
165                         return errors.Wrap(err, "reading input commitment type")
166                 }
167                 switch icType[0] {
168                 case IssuanceInputType:
169                         ii := new(IssuanceInput)
170                         t.TypedInput = ii
171
172                         if ii.Nonce, err = blockchain.ReadVarstr31(r); err != nil {
173                                 return err
174                         }
175                         if _, err = assetID.ReadFrom(r); err != nil {
176                                 return err
177                         }
178                         if ii.Amount, err = blockchain.ReadVarint63(r); err != nil {
179                                 return err
180                         }
181
182                 case SpendInputType:
183                         si := new(SpendInput)
184                         t.TypedInput = si
185                         if si.SpendCommitmentSuffix, err = si.SpendCommitment.readFrom(r, 1); err != nil {
186                                 return err
187                         }
188                 case ClainPeginInputType:
189                         ci := new(ClaimInput)
190                         t.TypedInput = ci
191                         if ci.SpendCommitmentSuffix, err = ci.SpendCommitment.readFrom(r, 1); err != nil {
192                                 return err
193                         }
194                 case CoinbaseInputType:
195                         ci := new(CoinbaseInput)
196                         t.TypedInput = ci
197                         if ci.Arbitrary, err = blockchain.ReadVarstr31(r); err != nil {
198                                 return err
199                         }
200                 case DposInputType:
201                         ci := new(DposTx)
202                         t.TypedInput = ci
203                         if ci.SpendCommitmentSuffix, err = ci.SpendCommitment.readFrom(r, 1); err != nil {
204                                 return err
205                         }
206                 default:
207                         return fmt.Errorf("unsupported input type %d", icType[0])
208                 }
209                 return nil
210         })
211         if err != nil {
212                 return err
213         }
214
215         t.WitnessSuffix, err = blockchain.ReadExtensibleString(r, func(r *blockchain.Reader) error {
216                 if t.AssetVersion != 1 {
217                         return nil
218                 }
219
220                 switch inp := t.TypedInput.(type) {
221                 case *IssuanceInput:
222                         if inp.AssetDefinition, err = blockchain.ReadVarstr31(r); err != nil {
223                                 return err
224                         }
225                         if inp.VMVersion, err = blockchain.ReadVarint63(r); err != nil {
226                                 return err
227                         }
228                         if inp.IssuanceProgram, err = blockchain.ReadVarstr31(r); err != nil {
229                                 return err
230                         }
231                         if inp.AssetID() != assetID {
232                                 return errBadAssetID
233                         }
234                         if inp.Arguments, err = blockchain.ReadVarstrList(r); err != nil {
235                                 return err
236                         }
237
238                 case *SpendInput:
239                         if inp.Arguments, err = blockchain.ReadVarstrList(r); err != nil {
240                                 return err
241                         }
242                 case *ClaimInput:
243                         if inp.Arguments, err = blockchain.ReadVarstrList(r); err != nil {
244                                 return err
245                         }
246                 case *DposTx:
247                         txType := uint64(0)
248                         if txType, err = blockchain.ReadVarint63(r); err != nil {
249                                 return err
250                         }
251                         inp.Type = TxType(txType)
252                         var from []byte
253                         if from, err = blockchain.ReadVarstr31(r); err != nil {
254                                 return err
255                         }
256                         inp.From = string(from)
257                         var to []byte
258                         if to, err = blockchain.ReadVarstr31(r); err != nil {
259                                 return err
260                         }
261                         inp.To = string(to)
262                         if inp.Amount, err = blockchain.ReadVarint63(r); err != nil {
263                                 return err
264                         }
265                         if inp.Stake, err = blockchain.ReadVarint63(r); err != nil {
266                                 return err
267                         }
268                         if inp.Arguments, err = blockchain.ReadVarstrList(r); err != nil {
269                                 return err
270                         }
271
272                         var info []byte
273                         if info, err = blockchain.ReadVarstr31(r); err != nil {
274                                 return err
275                         }
276                         inp.Info = string(info)
277                 }
278                 return nil
279         })
280
281         return err
282 }
283
284 func (t *TxInput) writeTo(w io.Writer) error {
285         if _, err := blockchain.WriteVarint63(w, t.AssetVersion); err != nil {
286                 return errors.Wrap(err, "writing asset version")
287         }
288
289         if _, err := blockchain.WriteVarstrList(w, t.Peginwitness); err != nil {
290                 return errors.Wrap(err, "writing pegin witness")
291         }
292         if _, err := blockchain.WriteExtensibleString(w, t.CommitmentSuffix, t.writeInputCommitment); err != nil {
293                 return errors.Wrap(err, "writing input commitment")
294         }
295
296         _, err := blockchain.WriteExtensibleString(w, t.WitnessSuffix, t.writeInputWitness)
297         return errors.Wrap(err, "writing input witness")
298 }
299
300 func (t *TxInput) writeInputCommitment(w io.Writer) (err error) {
301         if t.AssetVersion != 1 {
302                 return nil
303         }
304
305         switch inp := t.TypedInput.(type) {
306         case *IssuanceInput:
307                 if _, err = w.Write([]byte{IssuanceInputType}); err != nil {
308                         return err
309                 }
310                 if _, err = blockchain.WriteVarstr31(w, inp.Nonce); err != nil {
311                         return err
312                 }
313                 assetID := t.AssetID()
314                 if _, err = assetID.WriteTo(w); err != nil {
315                         return err
316                 }
317                 _, err = blockchain.WriteVarint63(w, inp.Amount)
318                 return err
319
320         case *SpendInput:
321                 if _, err = w.Write([]byte{SpendInputType}); err != nil {
322                         return err
323                 }
324                 return inp.SpendCommitment.writeExtensibleString(w, inp.SpendCommitmentSuffix, t.AssetVersion)
325         case *ClaimInput:
326                 if _, err = w.Write([]byte{ClainPeginInputType}); err != nil {
327                         return err
328                 }
329                 return inp.SpendCommitment.writeExtensibleString(w, inp.SpendCommitmentSuffix, t.AssetVersion)
330         case *CoinbaseInput:
331                 if _, err = w.Write([]byte{CoinbaseInputType}); err != nil {
332                         return err
333                 }
334                 if _, err = blockchain.WriteVarstr31(w, inp.Arbitrary); err != nil {
335                         return errors.Wrap(err, "writing coinbase arbitrary")
336                 }
337         case *DposTx:
338                 if _, err = w.Write([]byte{DposInputType}); err != nil {
339                         return err
340                 }
341                 return inp.SpendCommitment.writeExtensibleString(w, inp.SpendCommitmentSuffix, t.AssetVersion)
342         }
343         return nil
344 }
345
346 func (t *TxInput) writeInputWitness(w io.Writer) error {
347         if t.AssetVersion != 1 {
348                 return nil
349         }
350         switch inp := t.TypedInput.(type) {
351         case *IssuanceInput:
352                 if _, err := blockchain.WriteVarstr31(w, inp.AssetDefinition); err != nil {
353                         return err
354                 }
355                 if _, err := blockchain.WriteVarint63(w, inp.VMVersion); err != nil {
356                         return err
357                 }
358                 if _, err := blockchain.WriteVarstr31(w, inp.IssuanceProgram); err != nil {
359                         return err
360                 }
361                 _, err := blockchain.WriteVarstrList(w, inp.Arguments)
362                 return err
363
364         case *SpendInput:
365                 _, err := blockchain.WriteVarstrList(w, inp.Arguments)
366                 return err
367         case *ClaimInput:
368                 _, err := blockchain.WriteVarstrList(w, inp.Arguments)
369
370                 return err
371         case *DposTx:
372                 if _, err := blockchain.WriteVarint63(w, uint64(inp.Type)); err != nil {
373                         return err
374                 }
375                 if _, err := blockchain.WriteVarstr31(w, []byte(inp.From)); err != nil {
376                         return err
377                 }
378                 if _, err := blockchain.WriteVarstr31(w, []byte(inp.To)); err != nil {
379                         return err
380                 }
381                 if _, err := blockchain.WriteVarint63(w, inp.Amount); err != nil {
382                         return err
383                 }
384                 if _, err := blockchain.WriteVarint63(w, inp.Stake); err != nil {
385                         return err
386                 }
387                 if _, err := blockchain.WriteVarstrList(w, inp.Arguments); err != nil {
388                         return err
389                 }
390                 _, err := blockchain.WriteVarstr31(w, []byte(inp.Info))
391
392                 return err
393         }
394         return nil
395 }