argument.go 6.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
// 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"
22 23
	"reflect"
	"strings"
24 25 26 27 28
)

// Argument holds the name of the argument and the corresponding type.
// Types are used when packing and testing arguments.
type Argument struct {
29 30 31
	Name    string
	Type    Type
	Indexed bool // indexed is only used by events
32 33
}

34 35
type Arguments []Argument

36
// UnmarshalJSON implements json.Unmarshaler interface
37
func (argument *Argument) UnmarshalJSON(data []byte) error {
38
	var extarg struct {
F
Felix Lange 已提交
39 40
		Name    string
		Type    string
41
		Indexed bool
42 43 44 45 46 47
	}
	err := json.Unmarshal(data, &extarg)
	if err != nil {
		return fmt.Errorf("argument json err: %v", err)
	}

48
	argument.Type, err = NewType(extarg.Type)
49 50 51
	if err != nil {
		return err
	}
52 53
	argument.Name = extarg.Name
	argument.Indexed = extarg.Indexed
54 55 56

	return nil
}
57

58 59 60
// LengthNonIndexed returns the number of arguments when not counting 'indexed' ones. Only events
// can ever have 'indexed' arguments, it should always be false on arguments for method input/output
func (arguments Arguments) LengthNonIndexed() int {
61
	out := 0
62 63
	for _, arg := range arguments {
		if !arg.Indexed {
64 65 66 67 68
			out++
		}
	}
	return out
}
69 70 71 72

// isTuple returns true for non-atomic constructs, like (uint,uint) or uint[]
func (arguments Arguments) isTuple() bool {
	return len(arguments) > 1
73 74
}

75 76 77 78
// Unpack performs the operation hexdata -> Go format
func (arguments Arguments) Unpack(v interface{}, data []byte) error {
	if arguments.isTuple() {
		return arguments.unpackTuple(v, data)
79
	}
80
	return arguments.unpackAtomic(v, data)
81 82
}

83 84
func (arguments Arguments) unpackTuple(v interface{}, output []byte) error {
	// make sure the passed value is arguments pointer
85 86 87 88 89 90 91 92 93 94
	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()
		kind  = value.Kind()
	)
95 96

	if err := requireUnpackKind(value, typ, kind, arguments); err != nil {
97 98
		return err
	}
99 100 101 102 103 104 105 106 107 108 109 110 111 112
	// If the output interface is a struct, make sure names don't collide
	if kind == reflect.Struct {
		exists := make(map[string]bool)
		for _, arg := range arguments {
			field := capitalise(arg.Name)
			if field == "" {
				return fmt.Errorf("abi: purely underscored output cannot unpack to struct")
			}
			if exists[field] {
				return fmt.Errorf("abi: multiple outputs mapping to the same struct field '%s'", field)
			}
			exists[field] = true
		}
	}
113 114 115 116 117
	// `i` counts the nonindexed arguments.
	// `j` counts the number of complex types.
	// both `i` and `j` are used to to correctly compute `data` offset.

	i, j := -1, 0
118
	for _, arg := range arguments {
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139

		if arg.Indexed {
			// can't read, continue
			continue
		}
		i++
		marshalledValue, err := toGoType((i+j)*32, arg.Type, output)
		if err != nil {
			return err
		}

		if arg.Type.T == ArrayTy {
			// combined index ('i' + 'j') need to be adjusted only by size of array, thus
			// we need to decrement 'j' because 'i' was incremented
			j += arg.Type.Size - 1
		}

		reflectValue := reflect.ValueOf(marshalledValue)

		switch kind {
		case reflect.Struct:
140
			name := capitalise(arg.Name)
141 142
			for j := 0; j < typ.NumField(); j++ {
				// TODO read tags: `abi:"fieldName"`
143
				if typ.Field(j).Name == name {
144 145 146 147 148 149 150
					if err := set(value.Field(j), reflectValue, arg); err != nil {
						return err
					}
				}
			}
		case reflect.Slice, reflect.Array:
			if value.Len() < i {
151
				return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len())
152 153 154 155 156
			}
			v := value.Index(i)
			if err := requireAssignable(v, reflectValue); err != nil {
				return err
			}
157 158 159 160

			if err := set(v.Elem(), reflectValue, arg); err != nil {
				return err
			}
161 162 163 164 165 166 167
		default:
			return fmt.Errorf("abi:[2] cannot unmarshal tuple in to %v", typ)
		}
	}
	return nil
}

168 169 170
// unpackAtomic unpacks ( hexdata -> go ) a single value
func (arguments Arguments) unpackAtomic(v interface{}, output []byte) error {
	// make sure the passed value is arguments pointer
171 172 173 174
	valueOf := reflect.ValueOf(v)
	if reflect.Ptr != valueOf.Kind() {
		return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
	}
175
	arg := arguments[0]
176 177 178 179 180 181 182 183 184 185
	if arg.Indexed {
		return fmt.Errorf("abi: attempting to unpack indexed variable into element.")
	}

	value := valueOf.Elem()

	marshalledValue, err := toGoType(0, arg.Type, output)
	if err != nil {
		return err
	}
186
	return set(value, reflect.ValueOf(marshalledValue), arg)
187 188
}

189 190
// Unpack performs the operation Go format -> Hexdata
func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
191
	// Make sure arguments match up and pack them
192
	abiArgs := arguments
193 194 195 196 197 198 199 200 201 202 203 204
	if len(args) != len(abiArgs) {
		return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(abiArgs))
	}

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

	// input offset is the bytes offset for packed output
	inputOffset := 0
	for _, abiArg := range abiArgs {
		if abiArg.Type.T == ArrayTy {
205
			inputOffset += 32 * abiArg.Type.Size
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
		} else {
			inputOffset += 32
		}
	}

	var ret []byte
	for i, a := range args {
		input := abiArgs[i]
		// pack the input
		packed, err := input.Type.pack(reflect.ValueOf(a))
		if err != nil {
			return nil, err
		}

		// check for a slice type (string, bytes, slice)
		if input.Type.requiresLengthPrefix() {
			// calculate the offset
			offset := inputOffset + len(variableInput)
			// set the offset
			ret = append(ret, packNum(reflect.ValueOf(offset))...)
			// Append the packed output to the variable input. The variable input
			// will be appended at the end of the input.
			variableInput = append(variableInput, packed...)
		} else {
			// 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
}
239 240 241 242 243 244 245 246 247 248 249 250

// capitalise makes the first character of a string upper case, also removing any
// prefixing underscores from the variable names.
func capitalise(input string) string {
	for len(input) > 0 && input[0] == '_' {
		input = input[1:]
	}
	if len(input) == 0 {
		return ""
	}
	return strings.ToUpper(input[:1]) + input[1:]
}