OSDN Git Service

4fbcd18880e41c9ba9762065d1b4dd4c8630442f
[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.PaymentAmount, err = blockchain.ReadVarint63(r); err != nil {
269                                 return err
270                         }
271                         if inp.Arguments, err = blockchain.ReadVarstrList(r); err != nil {
272                                 return err
273                         }
274
275                         var info []byte
276                         if info, err = blockchain.ReadVarstr31(r); err != nil {
277                                 return err
278                         }
279                         inp.Info = string(info)
280                 }
281                 return nil
282         })
283
284         return err
285 }
286
287 func (t *TxInput) writeTo(w io.Writer) error {
288         if _, err := blockchain.WriteVarint63(w, t.AssetVersion); err != nil {
289                 return errors.Wrap(err, "writing asset version")
290         }
291
292         if _, err := blockchain.WriteVarstrList(w, t.Peginwitness); err != nil {
293                 return errors.Wrap(err, "writing pegin witness")
294         }
295         if _, err := blockchain.WriteExtensibleString(w, t.CommitmentSuffix, t.writeInputCommitment); err != nil {
296                 return errors.Wrap(err, "writing input commitment")
297         }
298
299         _, err := blockchain.WriteExtensibleString(w, t.WitnessSuffix, t.writeInputWitness)
300         return errors.Wrap(err, "writing input witness")
301 }
302
303 func (t *TxInput) writeInputCommitment(w io.Writer) (err error) {
304         if t.AssetVersion != 1 {
305                 return nil
306         }
307
308         switch inp := t.TypedInput.(type) {
309         case *IssuanceInput:
310                 if _, err = w.Write([]byte{IssuanceInputType}); err != nil {
311                         return err
312                 }
313                 if _, err = blockchain.WriteVarstr31(w, inp.Nonce); err != nil {
314                         return err
315                 }
316                 assetID := t.AssetID()
317                 if _, err = assetID.WriteTo(w); err != nil {
318                         return err
319                 }
320                 _, err = blockchain.WriteVarint63(w, inp.Amount)
321                 return err
322
323         case *SpendInput:
324                 if _, err = w.Write([]byte{SpendInputType}); err != nil {
325                         return err
326                 }
327                 return inp.SpendCommitment.writeExtensibleString(w, inp.SpendCommitmentSuffix, t.AssetVersion)
328         case *ClaimInput:
329                 if _, err = w.Write([]byte{ClainPeginInputType}); err != nil {
330                         return err
331                 }
332                 return inp.SpendCommitment.writeExtensibleString(w, inp.SpendCommitmentSuffix, t.AssetVersion)
333         case *CoinbaseInput:
334                 if _, err = w.Write([]byte{CoinbaseInputType}); err != nil {
335                         return err
336                 }
337                 if _, err = blockchain.WriteVarstr31(w, inp.Arbitrary); err != nil {
338                         return errors.Wrap(err, "writing coinbase arbitrary")
339                 }
340         case *DposTx:
341                 if _, err = w.Write([]byte{DposInputType}); err != nil {
342                         return err
343                 }
344                 return inp.SpendCommitment.writeExtensibleString(w, inp.SpendCommitmentSuffix, t.AssetVersion)
345         }
346         return nil
347 }
348
349 func (t *TxInput) writeInputWitness(w io.Writer) error {
350         if t.AssetVersion != 1 {
351                 return nil
352         }
353         switch inp := t.TypedInput.(type) {
354         case *IssuanceInput:
355                 if _, err := blockchain.WriteVarstr31(w, inp.AssetDefinition); err != nil {
356                         return err
357                 }
358                 if _, err := blockchain.WriteVarint63(w, inp.VMVersion); err != nil {
359                         return err
360                 }
361                 if _, err := blockchain.WriteVarstr31(w, inp.IssuanceProgram); err != nil {
362                         return err
363                 }
364                 _, err := blockchain.WriteVarstrList(w, inp.Arguments)
365                 return err
366
367         case *SpendInput:
368                 _, err := blockchain.WriteVarstrList(w, inp.Arguments)
369                 return err
370         case *ClaimInput:
371                 _, err := blockchain.WriteVarstrList(w, inp.Arguments)
372
373                 return err
374         case *DposTx:
375                 if _, err := blockchain.WriteVarint63(w, uint64(inp.Type)); err != nil {
376                         return err
377                 }
378                 if _, err := blockchain.WriteVarstr31(w, []byte(inp.From)); err != nil {
379                         return err
380                 }
381                 if _, err := blockchain.WriteVarstr31(w, []byte(inp.To)); err != nil {
382                         return err
383                 }
384                 if _, err := blockchain.WriteVarint63(w, inp.Amount); err != nil {
385                         return err
386                 }
387                 if _, err := blockchain.WriteVarint63(w, inp.Stake); err != nil {
388                         return err
389                 }
390                 if _, err := blockchain.WriteVarint63(w, inp.PaymentAmount); err != nil {
391                         return err
392                 }
393                 if _, err := blockchain.WriteVarstrList(w, inp.Arguments); err != nil {
394                         return err
395                 }
396                 _, err := blockchain.WriteVarstr31(w, []byte(inp.Info))
397
398                 return err
399         }
400         return nil
401 }