OSDN Git Service

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