OSDN Git Service

feat: add cross-chain output (#56)
[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 )
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() bc.AssetAmount {
38         switch inp := t.TypedInput.(type) {
39         case *IssuanceInput:
40                 assetID := inp.AssetID()
41                 return bc.AssetAmount{
42                         AssetId: &assetID,
43                         Amount:  inp.Amount,
44                 }
45         case *SpendInput:
46                 return inp.AssetAmount
47         }
48         return bc.AssetAmount{}
49 }
50
51 // AssetID return the assetID of the txinput
52 func (t *TxInput) AssetID() bc.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 bc.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 // AssetDefinition return the asset definition of the issuance input
91 func (t *TxInput) AssetDefinition() []byte {
92         if ii, ok := t.TypedInput.(*IssuanceInput); ok {
93                 return ii.AssetDefinition
94         }
95         return nil
96 }
97
98 // Arguments get the args for the input
99 func (t *TxInput) Arguments() [][]byte {
100         switch inp := t.TypedInput.(type) {
101         case *IssuanceInput:
102                 return inp.Arguments
103         case *SpendInput:
104                 return inp.Arguments
105         }
106         return nil
107 }
108
109 // SetArguments set the args for the input
110 func (t *TxInput) SetArguments(args [][]byte) {
111         switch inp := t.TypedInput.(type) {
112         case *IssuanceInput:
113                 inp.Arguments = args
114         case *SpendInput:
115                 inp.Arguments = args
116         }
117 }
118
119 // SpentOutputID calculate the hash of spended output
120 func (t *TxInput) SpentOutputID() (o bc.Hash, err error) {
121         if si, ok := t.TypedInput.(*SpendInput); ok {
122                 o, err = ComputeOutputID(&si.SpendCommitment)
123         }
124         return o, err
125 }
126
127 func (t *TxInput) readFrom(r *blockchain.Reader) (err error) {
128         if t.AssetVersion, err = blockchain.ReadVarint63(r); err != nil {
129                 return err
130         }
131
132         var assetID bc.AssetID
133         t.CommitmentSuffix, err = blockchain.ReadExtensibleString(r, func(r *blockchain.Reader) error {
134                 if t.AssetVersion != 1 {
135                         return nil
136                 }
137                 var icType [1]byte
138                 if _, err = io.ReadFull(r, icType[:]); err != nil {
139                         return errors.Wrap(err, "reading input commitment type")
140                 }
141                 switch icType[0] {
142                 case IssuanceInputType:
143                         ii := new(IssuanceInput)
144                         t.TypedInput = ii
145
146                         if ii.Nonce, err = blockchain.ReadVarstr31(r); err != nil {
147                                 return err
148                         }
149                         if _, err = assetID.ReadFrom(r); err != nil {
150                                 return err
151                         }
152                         if ii.Amount, err = blockchain.ReadVarint63(r); err != nil {
153                                 return err
154                         }
155
156                 case SpendInputType:
157                         si := new(SpendInput)
158                         t.TypedInput = si
159                         if si.SpendCommitmentSuffix, err = si.SpendCommitment.readFrom(r, 1); err != nil {
160                                 return err
161                         }
162
163                 case CoinbaseInputType:
164                         ci := new(CoinbaseInput)
165                         t.TypedInput = ci
166                         if ci.Arbitrary, err = blockchain.ReadVarstr31(r); err != nil {
167                                 return err
168                         }
169
170                 default:
171                         return fmt.Errorf("unsupported input type %d", icType[0])
172                 }
173                 return nil
174         })
175         if err != nil {
176                 return err
177         }
178
179         t.WitnessSuffix, err = blockchain.ReadExtensibleString(r, func(r *blockchain.Reader) error {
180                 if t.AssetVersion != 1 {
181                         return nil
182                 }
183
184                 switch inp := t.TypedInput.(type) {
185                 case *IssuanceInput:
186                         if inp.AssetDefinition, err = blockchain.ReadVarstr31(r); err != nil {
187                                 return err
188                         }
189                         if inp.VMVersion, err = blockchain.ReadVarint63(r); err != nil {
190                                 return err
191                         }
192                         if inp.IssuanceProgram, err = blockchain.ReadVarstr31(r); err != nil {
193                                 return err
194                         }
195                         if inp.AssetID() != assetID {
196                                 return errBadAssetID
197                         }
198                         if inp.Arguments, err = blockchain.ReadVarstrList(r); err != nil {
199                                 return err
200                         }
201
202                 case *SpendInput:
203                         if inp.Arguments, err = blockchain.ReadVarstrList(r); err != nil {
204                                 return err
205                         }
206                 }
207                 return nil
208         })
209
210         return err
211 }
212
213 func (t *TxInput) writeTo(w io.Writer) error {
214         if _, err := blockchain.WriteVarint63(w, t.AssetVersion); err != nil {
215                 return errors.Wrap(err, "writing asset version")
216         }
217
218         if _, err := blockchain.WriteExtensibleString(w, t.CommitmentSuffix, t.writeInputCommitment); err != nil {
219                 return errors.Wrap(err, "writing input commitment")
220         }
221
222         _, err := blockchain.WriteExtensibleString(w, t.WitnessSuffix, t.writeInputWitness)
223         return errors.Wrap(err, "writing input witness")
224 }
225
226 func (t *TxInput) writeInputCommitment(w io.Writer) (err error) {
227         if t.AssetVersion != 1 {
228                 return nil
229         }
230
231         switch inp := t.TypedInput.(type) {
232         case *IssuanceInput:
233                 if _, err = w.Write([]byte{IssuanceInputType}); 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{SpendInputType}); err != nil {
248                         return err
249                 }
250                 return inp.SpendCommitment.writeExtensibleString(w, inp.SpendCommitmentSuffix, t.AssetVersion)
251
252         case *CoinbaseInput:
253                 if _, err = w.Write([]byte{CoinbaseInputType}); err != nil {
254                         return err
255                 }
256                 if _, err = blockchain.WriteVarstr31(w, inp.Arbitrary); err != nil {
257                         return errors.Wrap(err, "writing coinbase arbitrary")
258                 }
259         }
260         return nil
261 }
262
263 func (t *TxInput) writeInputWitness(w io.Writer) error {
264         if t.AssetVersion != 1 {
265                 return nil
266         }
267
268         switch inp := t.TypedInput.(type) {
269         case *IssuanceInput:
270                 if _, err := blockchain.WriteVarstr31(w, inp.AssetDefinition); err != nil {
271                         return err
272                 }
273                 if _, err := blockchain.WriteVarint63(w, inp.VMVersion); err != nil {
274                         return err
275                 }
276                 if _, err := blockchain.WriteVarstr31(w, inp.IssuanceProgram); err != nil {
277                         return err
278                 }
279                 _, err := blockchain.WriteVarstrList(w, inp.Arguments)
280                 return err
281
282         case *SpendInput:
283                 _, err := blockchain.WriteVarstrList(w, inp.Arguments)
284                 return err
285         }
286         return nil
287 }