OSDN Git Service

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