OSDN Git Service

2da013452449c1a1eca953361ea049a01bfbf77c
[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         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 *UnvoteInput:
44                 return inp.AssetAmount
45         }
46         return bc.AssetAmount{}
47 }
48
49 // AssetID return the assetID of the txinput
50 func (t *TxInput) AssetID() bc.AssetID {
51         switch inp := t.TypedInput.(type) {
52         case *SpendInput:
53                 return *inp.AssetId
54
55         case *UnvoteInput:
56                 return *inp.AssetId
57         }
58         return bc.AssetID{}
59 }
60
61 // Amount return the asset amount of the txinput
62 func (t *TxInput) Amount() uint64 {
63         switch inp := t.TypedInput.(type) {
64         case *SpendInput:
65                 return inp.Amount
66
67         case *UnvoteInput:
68                 return inp.Amount
69         }
70         return 0
71 }
72
73 // ControlProgram return the control program of the spend input
74 func (t *TxInput) ControlProgram() []byte {
75         switch inp := t.TypedInput.(type) {
76         case *SpendInput:
77                 return inp.ControlProgram
78
79         case *UnvoteInput:
80                 return inp.ControlProgram
81         }
82
83         return nil
84 }
85
86 // Arguments get the args for the input
87 func (t *TxInput) Arguments() [][]byte {
88         switch inp := t.TypedInput.(type) {
89         case *SpendInput:
90                 return inp.Arguments
91
92         case *UnvoteInput:
93                 return inp.Arguments
94         }
95         return nil
96 }
97
98 // SetArguments set the args for the input
99 func (t *TxInput) SetArguments(args [][]byte) {
100         switch inp := t.TypedInput.(type) {
101         case *SpendInput:
102                 inp.Arguments = args
103
104         case *UnvoteInput:
105                 inp.Arguments = args
106         }
107 }
108
109 // SpentOutputID calculate the hash of spended output
110 func (t *TxInput) SpentOutputID() (o bc.Hash, err error) {
111         switch inp := t.TypedInput.(type) {
112         case *SpendInput:
113                 o, err = ComputeOutputID(&inp.SpendCommitment, SpendInputType, nil)
114
115         case *UnvoteInput:
116                 o, err = ComputeOutputID(&inp.SpendCommitment, UnvoteInputType, inp.Vote)
117         }
118
119         return o, err
120 }
121
122 func (t *TxInput) readFrom(r *blockchain.Reader) (err error) {
123         if t.AssetVersion, err = blockchain.ReadVarint63(r); err != nil {
124                 return err
125         }
126
127         t.CommitmentSuffix, err = blockchain.ReadExtensibleString(r, func(r *blockchain.Reader) error {
128                 if t.AssetVersion != 1 {
129                         return nil
130                 }
131                 var icType [1]byte
132                 if _, err = io.ReadFull(r, icType[:]); err != nil {
133                         return errors.Wrap(err, "reading input commitment type")
134                 }
135                 switch icType[0] {
136                 case SpendInputType:
137                         si := new(SpendInput)
138                         t.TypedInput = si
139                         if si.SpendCommitmentSuffix, err = si.SpendCommitment.readFrom(r, 1); err != nil {
140                                 return err
141                         }
142
143                 case CoinbaseInputType:
144                         ci := new(CoinbaseInput)
145                         t.TypedInput = ci
146                         if ci.Arbitrary, err = blockchain.ReadVarstr31(r); err != nil {
147                                 return err
148                         }
149
150                 case UnvoteInputType:
151                         ui := new(UnvoteInput)
152                         t.TypedInput = ui
153                         if ui.UnvoteCommitmentSuffix, err = ui.SpendCommitment.readFrom(r, 1); err != nil {
154                                 return err
155                         }
156
157                 default:
158                         return fmt.Errorf("unsupported input type %d", icType[0])
159                 }
160                 return nil
161         })
162         if err != nil {
163                 return err
164         }
165
166         t.WitnessSuffix, err = blockchain.ReadExtensibleString(r, func(r *blockchain.Reader) error {
167                 if t.AssetVersion != 1 {
168                         return nil
169                 }
170
171                 switch inp := t.TypedInput.(type) {
172                 case *SpendInput:
173                         if inp.Arguments, err = blockchain.ReadVarstrList(r); err != nil {
174                                 return err
175                         }
176
177                 case *UnvoteInput:
178                         if inp.Arguments, err = blockchain.ReadVarstrList(r); err != nil {
179                                 return err
180                         }
181                         if inp.Vote, err = blockchain.ReadVarstr31(r); err != nil {
182                                 return err
183                         }
184
185                 }
186                 return nil
187         })
188
189         return err
190 }
191
192 func (t *TxInput) writeTo(w io.Writer) error {
193         if _, err := blockchain.WriteVarint63(w, t.AssetVersion); err != nil {
194                 return errors.Wrap(err, "writing asset version")
195         }
196
197         if _, err := blockchain.WriteExtensibleString(w, t.CommitmentSuffix, t.writeInputCommitment); err != nil {
198                 return errors.Wrap(err, "writing input commitment")
199         }
200
201         _, err := blockchain.WriteExtensibleString(w, t.WitnessSuffix, t.writeInputWitness)
202         return errors.Wrap(err, "writing input witness")
203 }
204
205 func (t *TxInput) writeInputCommitment(w io.Writer) (err error) {
206         if t.AssetVersion != 1 {
207                 return nil
208         }
209
210         switch inp := t.TypedInput.(type) {
211         case *SpendInput:
212                 if _, err = w.Write([]byte{SpendInputType}); err != nil {
213                         return err
214                 }
215                 return inp.SpendCommitment.writeExtensibleString(w, inp.SpendCommitmentSuffix, t.AssetVersion)
216
217         case *CoinbaseInput:
218                 if _, err = w.Write([]byte{CoinbaseInputType}); err != nil {
219                         return err
220                 }
221                 if _, err = blockchain.WriteVarstr31(w, inp.Arbitrary); err != nil {
222                         return errors.Wrap(err, "writing coinbase arbitrary")
223                 }
224
225         case *UnvoteInput:
226                 if _, err = w.Write([]byte{UnvoteInputType}); err != nil {
227                         return err
228                 }
229                 return inp.SpendCommitment.writeExtensibleString(w, inp.UnvoteCommitmentSuffix, t.AssetVersion)
230         }
231         return nil
232 }
233
234 func (t *TxInput) writeInputWitness(w io.Writer) error {
235         if t.AssetVersion != 1 {
236                 return nil
237         }
238
239         switch inp := t.TypedInput.(type) {
240         case *SpendInput:
241                 _, err := blockchain.WriteVarstrList(w, inp.Arguments)
242                 return err
243
244         case *UnvoteInput:
245                 if _, err := blockchain.WriteVarstrList(w, inp.Arguments); err != nil {
246                         return err
247                 }
248                 _, err := blockchain.WriteVarstr31(w, inp.Vote)
249                 return err
250         }
251         return nil
252 }