OSDN Git Service

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