aboutsummaryrefslogtreecommitdiffstats
path: root/accounts/abi/abi.go
blob: 627a2a0c4cc3625a2d464ae9f5534cfeee065660 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package abi

import (
    "encoding/json"
    "fmt"
    "io"
    "math/big"
    "reflect"
    "strings"

    "github.com/ethereum/go-ethereum/common"
)

// The ABI holds information about a contract's context and available
// invokable methods. It will allow you to type check function calls and
// packs data accordingly.
type ABI struct {
    Constructor Method
    Methods     map[string]Method
    Events      map[string]Event
}

// JSON returns a parsed ABI interface and error if it failed.
func JSON(reader io.Reader) (ABI, error) {
    dec := json.NewDecoder(reader)

    var abi ABI
    if err := dec.Decode(&abi); err != nil {
        return ABI{}, err
    }

    return abi, nil
}

// Pack the given method name to conform the ABI. Method call's data
// will consist of method_id, args0, arg1, ... argN. Method id consists
// of 4 bytes and arguments are all 32 bytes.
// Method ids are created from the first 4 bytes of the hash of the
// methods string signature. (signature = baz(uint32,string32))
func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
    // Fetch the ABI of the requested method
    var method Method

    if name == "" {
        method = abi.Constructor
    } else {
        m, exist := abi.Methods[name]
        if !exist {
            return nil, fmt.Errorf("method '%s' not found", name)
        }
        method = m
    }
    arguments, err := method.pack(method, args...)
    if err != nil {
        return nil, err
    }
    // Pack up the method ID too if not a constructor and return
    if name == "" {
        return arguments, nil
    }
    return append(method.Id(), arguments...), nil
}

// toGoSliceType parses the input and casts it to the proper slice defined by the ABI
// argument in T.
func toGoSlice(i int, t Argument, output []byte) (interface{}, error) {
    index := i * 32
    // The slice must, at very least be large enough for the index+32 which is exactly the size required
    // for the [offset in output, size of offset].
    if index+32 > len(output) {
        return nil, fmt.Errorf("abi: cannot marshal in to go slice: insufficient size output %d require %d", len(output), index+32)
    }
    elem := t.Type.Elem

    // first we need to create a slice of the type
    var refSlice reflect.Value
    switch elem.T {
    case IntTy, UintTy, BoolTy:
        // create a new reference slice matching the element type
        switch t.Type.Kind {
        case reflect.Bool:
            refSlice = reflect.ValueOf([]bool(nil))
        case reflect.Uint8:
            refSlice = reflect.ValueOf([]uint8(nil))
        case reflect.Uint16:
            refSlice = reflect.ValueOf([]uint16(nil))
        case reflect.Uint32:
            refSlice = reflect.ValueOf([]uint32(nil))
        case reflect.Uint64:
            refSlice = reflect.ValueOf([]uint64(nil))
        case reflect.Int8:
            refSlice = reflect.ValueOf([]int8(nil))
        case reflect.Int16:
            refSlice = reflect.ValueOf([]int16(nil))
        case reflect.Int32:
            refSlice = reflect.ValueOf([]int32(nil))
        case reflect.Int64:
            refSlice = reflect.ValueOf([]int64(nil))
        default:
            refSlice = reflect.ValueOf([]*big.Int(nil))
        }
    case AddressTy: // address must be of slice Address
        refSlice = reflect.ValueOf([]common.Address(nil))
    case HashTy: // hash must be of slice hash
        refSlice = reflect.ValueOf([]common.Hash(nil))
    case FixedBytesTy:
        refSlice = reflect.ValueOf([][]byte(nil))
    default: // no other types are supported
        return nil, fmt.Errorf("abi: unsupported slice type %v", elem.T)
    }

    var slice []byte
    var size int
    var offset int
    if t.Type.IsSlice {

        // get the offset which determines the start of this array ...
        offset = int(common.BytesToBig(output[index : index+32]).Uint64())
        if offset+32 > len(output) {
            return nil, fmt.Errorf("abi: cannot marshal in to go slice: offset %d would go over slice boundary (len=%d)", len(output), offset+32)
        }

        slice = output[offset:]
        // ... starting with the size of the array in elements ...
        size = int(common.BytesToBig(slice[:32]).Uint64())
        slice = slice[32:]
        // ... and make sure that we've at the very least the amount of bytes
        // available in the buffer.
        if size*32 > len(slice) {
            return nil, fmt.Errorf("abi: cannot marshal in to go slice: insufficient size output %d require %d", len(output), offset+32+size*32)
        }

        // reslice to match the required size
        slice = slice[:(size * 32)]
    } else if t.Type.IsArray {
        //get the number of elements in the array
        size = t.Type.SliceSize

        //check to make sure array size matches up
        if index+32*size > len(output) {
            return nil, fmt.Errorf("abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)", len(output), index+32*size)
        }
        //slice is there for a fixed amount of times
        slice = output[index : index+size*32]
    }

    for i := 0; i < size; i++ {
        var (
            inter        interface{}             // interface type
            returnOutput = slice[i*32 : i*32+32] // the return output
        )

        // set inter to the correct type (cast)
        switch elem.T {
        case IntTy, UintTy:
            bigNum := common.BytesToBig(returnOutput)
            switch t.Type.Kind {
            case reflect.Uint8:
                inter = uint8(bigNum.Uint64())
            case reflect.Uint16:
                inter = uint16(bigNum.Uint64())
            case reflect.Uint32:
                inter = uint32(bigNum.Uint64())
            case reflect.Uint64:
                inter = bigNum.Uint64()
            case reflect.Int8:
                inter = int8(bigNum.Int64())
            case reflect.Int16:
                inter = int16(bigNum.Int64())
            case reflect.Int32:
                inter = int32(bigNum.Int64())
            case reflect.Int64:
                inter = bigNum.Int64()
            default:
                inter = common.BytesToBig(returnOutput)
            }
        case BoolTy:
            inter = common.BytesToBig(returnOutput).Uint64() > 0
        case AddressTy:
            inter = common.BytesToAddress(returnOutput)
        case HashTy:
            inter = common.BytesToHash(returnOutput)
        case FixedBytesTy:
            inter = returnOutput
        }
        // append the item to our reflect slice
        refSlice = reflect.Append(refSlice, reflect.ValueOf(inter))
    }

    // return the interface
    return refSlice.Interface(), nil
}

// toGoType parses the input and casts it to the proper type defined by the ABI
// argument in T.
func toGoType(i int, t Argument, output []byte) (interface{}, error) {
    // we need to treat slices differently
    if (t.Type.IsSlice || t.Type.IsArray) && t.Type.T != BytesTy && t.Type.T != StringTy && t.Type.T != FixedBytesTy && t.Type.T != FunctionTy {
        return toGoSlice(i, t, output)
    }

    index := i * 32
    if index+32 > len(output) {
        return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), index+32)
    }

    // Parse the given index output and check whether we need to read
    // a different offset and length based on the type (i.e. string, bytes)
    var returnOutput []byte
    switch t.Type.T {
    case StringTy, BytesTy: // variable arrays are written at the end of the return bytes
        // parse offset from which we should start reading
        offset := int(common.BytesToBig(output[index : index+32]).Uint64())
        if offset+32 > len(output) {
            return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32)
        }
        // parse the size up until we should be reading
        size := int(common.BytesToBig(output[offset : offset+32]).Uint64())
        if offset+32+size > len(output) {
            return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32+size)
        }

        // get the bytes for this return value
        returnOutput = output[offset+32 : offset+32+size]
    default:
        returnOutput = output[index : index+32]
    }

    // convert the bytes to whatever is specified by the ABI.
    switch t.Type.T {
    case IntTy, UintTy:
        bigNum := common.BytesToBig(returnOutput)

        // If the type is a integer convert to the integer type
        // specified by the ABI.
        switch t.Type.Kind {
        case reflect.Uint8:
            return uint8(bigNum.Uint64()), nil
        case reflect.Uint16:
            return uint16(bigNum.Uint64()), nil
        case reflect.Uint32:
            return uint32(bigNum.Uint64()), nil
        case reflect.Uint64:
            return uint64(bigNum.Uint64()), nil
        case reflect.Int8:
            return int8(bigNum.Int64()), nil
        case reflect.Int16:
            return int16(bigNum.Int64()), nil
        case reflect.Int32:
            return int32(bigNum.Int64()), nil
        case reflect.Int64:
            return int64(bigNum.Int64()), nil
        case reflect.Ptr:
            return bigNum, nil
        }
    case BoolTy:
        return common.BytesToBig(returnOutput).Uint64() > 0, nil
    case AddressTy:
        return common.BytesToAddress(returnOutput), nil
    case HashTy:
        return common.BytesToHash(returnOutput), nil
    case BytesTy, FixedBytesTy, FunctionTy:
        return returnOutput, nil
    case StringTy:
        return string(returnOutput), nil
    }
    return nil, fmt.Errorf("abi: unknown type %v", t.Type.T)
}

// these variable are used to determine certain types during type assertion for
// assignment.
var (
    r_interSlice = reflect.TypeOf([]interface{}{})
    r_hash       = reflect.TypeOf(common.Hash{})
    r_bytes      = reflect.TypeOf([]byte{})
    r_byte       = reflect.TypeOf(byte(0))
)

// Unpack output in v according to the abi specification
func (abi ABI) Unpack(v interface{}, name string, output []byte) error {
    var method = abi.Methods[name]

    if len(output) == 0 {
        return fmt.Errorf("abi: unmarshalling empty output")
    }

    // make sure the passed value is a pointer
    valueOf := reflect.ValueOf(v)
    if reflect.Ptr != valueOf.Kind() {
        return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
    }

    var (
        value = valueOf.Elem()
        typ   = value.Type()
    )

    if len(method.Outputs) > 1 {
        switch value.Kind() {
        // struct will match named return values to the struct's field
        // names
        case reflect.Struct:
            for i := 0; i < len(method.Outputs); i++ {
                marshalledValue, err := toGoType(i, method.Outputs[i], output)
                if err != nil {
                    return err
                }
                reflectValue := reflect.ValueOf(marshalledValue)

                for j := 0; j < typ.NumField(); j++ {
                    field := typ.Field(j)
                    // TODO read tags: `abi:"fieldName"`
                    if field.Name == strings.ToUpper(method.Outputs[i].Name[:1])+method.Outputs[i].Name[1:] {
                        if err := set(value.Field(j), reflectValue, method.Outputs[i]); err != nil {
                            return err
                        }
                    }
                }
            }
        case reflect.Slice:
            if !value.Type().AssignableTo(r_interSlice) {
                return fmt.Errorf("abi: cannot marshal tuple in to slice %T (only []interface{} is supported)", v)
            }

            // if the slice already contains values, set those instead of the interface slice itself.
            if value.Len() > 0 {
                if len(method.Outputs) > value.Len() {
                    return fmt.Errorf("abi: cannot marshal in to slices of unequal size (require: %v, got: %v)", len(method.Outputs), value.Len())
                }

                for i := 0; i < len(method.Outputs); i++ {
                    marshalledValue, err := toGoType(i, method.Outputs[i], output)
                    if err != nil {
                        return err
                    }
                    reflectValue := reflect.ValueOf(marshalledValue)
                    if err := set(value.Index(i).Elem(), reflectValue, method.Outputs[i]); err != nil {
                        return err
                    }
                }
                return nil
            }

            // create a new slice and start appending the unmarshalled
            // values to the new interface slice.
            z := reflect.MakeSlice(typ, 0, len(method.Outputs))
            for i := 0; i < len(method.Outputs); i++ {
                marshalledValue, err := toGoType(i, method.Outputs[i], output)
                if err != nil {
                    return err
                }
                z = reflect.Append(z, reflect.ValueOf(marshalledValue))
            }
            value.Set(z)
        default:
            return fmt.Errorf("abi: cannot unmarshal tuple in to %v", typ)
        }

    } else {
        marshalledValue, err := toGoType(0, method.Outputs[0], output)
        if err != nil {
            return err
        }
        if err := set(value, reflect.ValueOf(marshalledValue), method.Outputs[0]); err != nil {
            return err
        }
    }

    return nil
}

func (abi *ABI) UnmarshalJSON(data []byte) error {
    var fields []struct {
        Type      string
        Name      string
        Constant  bool
        Indexed   bool
        Anonymous bool
        Inputs    []Argument
        Outputs   []Argument
    }

    if err := json.Unmarshal(data, &fields); err != nil {
        return err
    }

    abi.Methods = make(map[string]Method)
    abi.Events = make(map[string]Event)
    for _, field := range fields {
        switch field.Type {
        case "constructor":
            abi.Constructor = Method{
                Inputs: field.Inputs,
            }
        // empty defaults to function according to the abi spec
        case "function", "":
            abi.Methods[field.Name] = Method{
                Name:    field.Name,
                Const:   field.Constant,
                Inputs:  field.Inputs,
                Outputs: field.Outputs,
            }
        case "event":
            abi.Events[field.Name] = Event{
                Name:      field.Name,
                Anonymous: field.Anonymous,
                Inputs:    field.Inputs,
            }
        }
    }

    return nil
}