OSDN Git Service

update master (#487)
[bytom/bytom-spv.git] / protocol / bc / types / txinput.go
1 package types
2
3 import (
4         "fmt"
5         "io"
6
7         "github.com/bytom/encoding/blockchain"
8         "github.com/bytom/errors"
9         "github.com/bytom/protocol/bc"
10 )
11
12 type (
13         TxInput struct {
14                 AssetVersion  uint64
15                 TypedInput
16
17                 // Unconsumed suffixes of the commitment and witness extensible
18                 // strings.
19                 CommitmentSuffix []byte
20                 WitnessSuffix    []byte
21         }
22
23         TypedInput interface {
24                 IsIssuance() bool
25                 IsCoinbase() bool
26         }
27 )
28
29 var errBadAssetID = errors.New("asset ID does not match other issuance parameters")
30
31 func (t *TxInput) AssetAmount() bc.AssetAmount {
32         if ii, ok := t.TypedInput.(*IssuanceInput); ok {
33                 assetID := ii.AssetID()
34                 return bc.AssetAmount{
35                         AssetId: &assetID,
36                         Amount:  ii.Amount,
37                 }
38         }
39         si := t.TypedInput.(*SpendInput)
40         return si.AssetAmount
41 }
42
43 func (t *TxInput) AssetID() bc.AssetID {
44         if ii, ok := t.TypedInput.(*IssuanceInput); ok {
45                 return ii.AssetID()
46         }
47         si := t.TypedInput.(*SpendInput)
48         return *si.AssetId
49 }
50
51 func (t *TxInput) Amount() uint64 {
52         if ii, ok := t.TypedInput.(*IssuanceInput); ok {
53                 return ii.Amount
54         }
55         si := t.TypedInput.(*SpendInput)
56         return si.Amount
57 }
58
59 func (t *TxInput) ControlProgram() []byte {
60         if si, ok := t.TypedInput.(*SpendInput); ok {
61                 return si.ControlProgram
62         }
63         return nil
64 }
65
66 func (t *TxInput) IssuanceProgram() []byte {
67         if ii, ok := t.TypedInput.(*IssuanceInput); ok {
68                 return ii.IssuanceProgram
69         }
70         return nil
71 }
72
73 func (t *TxInput) Arguments() [][]byte {
74         switch inp := t.TypedInput.(type) {
75         case *IssuanceInput:
76                 return inp.Arguments
77         case *SpendInput:
78                 return inp.Arguments
79         }
80         return nil
81 }
82
83 func (t *TxInput) SetArguments(args [][]byte) {
84         switch inp := t.TypedInput.(type) {
85         case *IssuanceInput:
86                 inp.Arguments = args
87         case *SpendInput:
88                 inp.Arguments = args
89         }
90 }
91
92 func (t *TxInput) readFrom(r *blockchain.Reader) (err error) {
93         t.AssetVersion, err = blockchain.ReadVarint63(r)
94         if err != nil {
95                 return err
96         }
97
98         var (
99                 ci      *CoinbaseInput
100                 ii      *IssuanceInput
101                 si      *SpendInput
102                 assetID bc.AssetID
103         )
104
105         t.CommitmentSuffix, err = blockchain.ReadExtensibleString(r, func(r *blockchain.Reader) error {
106                 if t.AssetVersion != 1 {
107                         return nil
108                 }
109                 var icType [1]byte
110                 if _, err = io.ReadFull(r, icType[:]); err != nil {
111                         return errors.Wrap(err, "reading input commitment type")
112                 }
113                 switch icType[0] {
114                 case 0:
115                         ii = new(IssuanceInput)
116
117                         ii.Nonce, err = blockchain.ReadVarstr31(r)
118                         if err != nil {
119                                 return err
120                         }
121                         if _, err = assetID.ReadFrom(r); err != nil {
122                                 return err
123                         }
124                         ii.Amount, err = blockchain.ReadVarint63(r)
125                         if err != nil {
126                                 return err
127                         }
128
129                 case 1:
130                         si = new(SpendInput)
131                         si.SpendCommitmentSuffix, err = si.SpendCommitment.readFrom(r, 1)
132                         if err != nil {
133                                 return err
134                         }
135
136                 case 2:
137                         ci = new(CoinbaseInput)
138                         if ci.Arbitrary, err = blockchain.ReadVarstr31(r); err != nil {
139                                 return err
140                         }
141
142                 default:
143                         return fmt.Errorf("unsupported input type %d", icType[0])
144                 }
145                 return nil
146         })
147         if err != nil {
148                 return err
149         }
150         t.WitnessSuffix, err = blockchain.ReadExtensibleString(r, func(r *blockchain.Reader) error {
151                 // TODO(bobg): test that serialization flags include SerWitness, when we relax the serflags-must-be-0x7 rule
152                 if t.AssetVersion != 1 {
153                         return nil
154                 }
155
156                 if ci != nil {
157                         return nil
158                 }
159
160                 if ii != nil {
161                         // read IssuanceInput witness
162                         ii.AssetDefinition, err = blockchain.ReadVarstr31(r)
163                         if err != nil {
164                                 return err
165                         }
166
167                         ii.VMVersion, err = blockchain.ReadVarint63(r)
168                         if err != nil {
169                                 return err
170                         }
171
172                         ii.IssuanceProgram, err = blockchain.ReadVarstr31(r)
173                         if err != nil {
174                                 return err
175                         }
176
177                         if ii.AssetID() != assetID {
178                                 return errBadAssetID
179                         }
180                 }
181                 args, err := blockchain.ReadVarstrList(r)
182                 if err != nil {
183                         return err
184                 }
185                 if ii != nil {
186                         ii.Arguments = args
187                 } else if si != nil {
188                         si.Arguments = args
189                 }
190                 return nil
191         })
192         if err != nil {
193                 return err
194         }
195         if ci != nil {
196                 t.TypedInput = ci
197         } else if ii != nil {
198                 t.TypedInput = ii
199         } else if si != nil {
200                 t.TypedInput = si
201         }
202         return nil
203 }
204
205 func (t *TxInput) writeTo(w io.Writer, serflags uint8) error {
206         if _, err := blockchain.WriteVarint63(w, t.AssetVersion); err != nil {
207                 return errors.Wrap(err, "writing asset version")
208         }
209
210         _, err := blockchain.WriteExtensibleString(w, t.CommitmentSuffix, func(w io.Writer) error {
211                 return t.WriteInputCommitment(w, serflags)
212         })
213         if err != nil {
214                 return errors.Wrap(err, "writing input commitment")
215         }
216
217
218         if serflags&SerWitness != 0 {
219                 if _, err = blockchain.WriteExtensibleString(w, t.WitnessSuffix, t.writeInputWitness); err != nil {
220                         return errors.Wrap(err, "writing input witness")
221                 }
222         }
223
224         return nil
225 }
226
227 func (t *TxInput) WriteInputCommitment(w io.Writer, serflags uint8) (err error) {
228         if t.AssetVersion != 1 {
229                 return nil
230         }
231         switch inp := t.TypedInput.(type) {
232         case *IssuanceInput:
233                 if _, err = w.Write([]byte{0}); err != nil {
234                         return err
235                 }
236                 if _, err = blockchain.WriteVarstr31(w, inp.Nonce); err != nil {
237                         return err
238                 }
239                 assetID := t.AssetID()
240                 if _, err = assetID.WriteTo(w); err != nil {
241                         return err
242                 }
243                 _, err = blockchain.WriteVarint63(w, inp.Amount)
244                 return err
245
246         case *SpendInput:
247                 if _, err = w.Write([]byte{1}); err != nil {
248                         return err
249                 }
250                 if serflags&SerPrevout != 0 {
251                         err = inp.SpendCommitment.writeExtensibleString(w, inp.SpendCommitmentSuffix, t.AssetVersion)
252                 } else {
253                         prevouthash := inp.SpendCommitment.Hash(inp.SpendCommitmentSuffix, t.AssetVersion)
254                         _, err = prevouthash.WriteTo(w)
255                 }
256                 return err
257
258         case *CoinbaseInput:
259                 if _, err = w.Write([]byte{2}); err != nil {
260                         return err
261                 }
262                 if _, err = blockchain.WriteVarstr31(w, inp.Arbitrary); err != nil {
263                         return errors.Wrap(err, "writing coinbase arbitrary")
264                 }
265         }
266         return nil
267 }
268
269 func (t *TxInput) writeInputWitness(w io.Writer) error {
270         if t.AssetVersion != 1 {
271                 return nil
272         }
273         switch inp := t.TypedInput.(type) {
274         case *IssuanceInput:
275                 _, err := blockchain.WriteVarstr31(w, inp.AssetDefinition)
276                 if err != nil {
277                         return err
278                 }
279                 _, err = blockchain.WriteVarint63(w, inp.VMVersion)
280                 if err != nil {
281                         return err
282                 }
283                 _, err = blockchain.WriteVarstr31(w, inp.IssuanceProgram)
284                 if err != nil {
285                         return err
286                 }
287                 _, err = blockchain.WriteVarstrList(w, inp.Arguments)
288                 return err
289
290         case *SpendInput:
291                 _, err := blockchain.WriteVarstrList(w, inp.Arguments)
292                 return err
293         }
294         return nil
295 }
296
297 func (t *TxInput) SpentOutputID() (o bc.Hash, err error) {
298         if si, ok := t.TypedInput.(*SpendInput); ok {
299                 o, err = ComputeOutputID(&si.SpendCommitment)
300         }
301         return o, err
302 }