aboutsummaryrefslogtreecommitdiffstats
path: root/accounts/abi/abi.go
blob: 029d1b102eabf44f45f46d53eab891b3f7564a10 (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
// 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"
    "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 {
    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
}

// tests, tests whether the given input would result in a successful
// call. Checks argument list count and matches input to `input`.
func (abi ABI) pack(name string, args ...interface{}) ([]byte, error) {
    method := abi.Methods[name]

    // variable input is the output appended at the end of packed
    // output. This is used for strings and bytes types input.
    var variableInput []byte

    var ret []byte
    for i, a := range args {
        input := method.Inputs[i]
        // pack the input
        packed, err := input.Type.pack(a)
        if err != nil {
            return nil, fmt.Errorf("`%s` %v", name, err)
        }

        // check for a string or bytes input type
        switch input.Type.T {
        case StringTy, BytesTy:
            // calculate the offset
            offset := len(method.Inputs)*32 + len(variableInput)
            // set the offset
            ret = append(ret, packNum(reflect.ValueOf(offset), UintTy)...)
            // Append the packed output to the variable input. The variable input
            // will be appended at the end of the input.
            variableInput = append(variableInput, packed...)
        default:
            // append the packed value to the input
            ret = append(ret, packed...)
        }
    }
    // append the variable input at the end of the packed input
    ret = append(ret, variableInput...)

    return ret, 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) {
    method, exist := abi.Methods[name]
    if !exist {
        return nil, fmt.Errorf("method '%s' not found", name)
    }

    // start with argument count match
    if len(args) != len(method.Inputs) {
        return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(method.Inputs))
    }

    arguments, err := abi.pack(name, args...)
    if err != nil {
        return nil, err
    }

    // Set function id
    packed := abi.Methods[name].Id()
    packed = append(packed, arguments...)

    return packed, 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) {
    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]
    }

    // cast bytes to abi return type
    switch t.Type.T {
    case IntTy:
        return common.BytesToBig(returnOutput), nil
    case UintTy:
        return common.BytesToBig(returnOutput), 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:
        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")
    }

    value := reflect.ValueOf(v).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)
            }

            // 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
}

// set attempts to assign src to dst by either setting, copying or otherwise.
//
// set is a bit more lenient when it comes to assignment and doesn't force an as
// strict ruleset as bare `reflect` does.
func set(dst, src reflect.Value, output Argument) error {
    dstType := dst.Type()
    srcType := src.Type()

    switch {
    case dstType.AssignableTo(src.Type()):
        dst.Set(src)
    case dstType.Kind() == reflect.Array && srcType.Kind() == reflect.Slice:
        if !dstType.Elem().AssignableTo(r_byte) {
            return fmt.Errorf("abi: cannot unmarshal %v in to array of elem %v", src.Type(), dstType.Elem())
        }

        if dst.Len() < output.Type.Size {
            return fmt.Errorf("abi: cannot unmarshal src (len=%d) in to dst (len=%d)", output.Type.Size, dst.Len())
        }
        reflect.Copy(dst, src)
    default:
        return fmt.Errorf("abi: cannot unmarshal %v in to %v", src.Type(), dst.Type())
    }
    return nil
}

func (abi *ABI) UnmarshalJSON(data []byte) error {
    var fields []struct {
        Type     string
        Name     string
        Constant bool
        Indexed  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 {
        // 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,
                Inputs: field.Inputs,
            }
        }
    }

    return nil
}