OSDN Git Service

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