| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // Copyright 2015 The go-ethereum Authors | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // This file is part of the go-ethereum library. | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2015-07-23 18:35:11 +02:00
										 |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // 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. | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-27 16:19:21 +01:00
										 |  |  | package abi | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"reflect" | 
					
						
							|  |  |  | 	"regexp" | 
					
						
							|  |  |  | 	"strconv" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-16 11:27:38 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							| 
									
										
										
										
											2015-01-27 16:19:21 +01:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	IntTy byte = iota | 
					
						
							|  |  |  | 	UintTy | 
					
						
							|  |  |  | 	BoolTy | 
					
						
							| 
									
										
										
										
											2016-02-09 13:57:00 +01:00
										 |  |  | 	StringTy | 
					
						
							| 
									
										
										
										
											2015-01-27 16:19:21 +01:00
										 |  |  | 	SliceTy | 
					
						
							|  |  |  | 	AddressTy | 
					
						
							| 
									
										
										
										
											2016-02-09 13:57:00 +01:00
										 |  |  | 	FixedBytesTy | 
					
						
							|  |  |  | 	BytesTy | 
					
						
							| 
									
										
										
										
											2015-11-20 13:45:37 +01:00
										 |  |  | 	HashTy | 
					
						
							| 
									
										
										
										
											2015-01-27 16:19:21 +01:00
										 |  |  | 	RealTy | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Type is the reflection of the supported argument type | 
					
						
							|  |  |  | type Type struct { | 
					
						
							|  |  |  | 	Kind       reflect.Kind | 
					
						
							|  |  |  | 	Type       reflect.Type | 
					
						
							|  |  |  | 	Size       int | 
					
						
							|  |  |  | 	T          byte   // Our own type checking | 
					
						
							|  |  |  | 	stringKind string // holds the unparsed string for deriving signatures | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-28 22:57:21 +01:00
										 |  |  | // NewType returns a fully parsed Type given by the input string or an error if it  can't be parsed. | 
					
						
							| 
									
										
										
										
											2015-01-27 16:19:21 +01:00
										 |  |  | // | 
					
						
							|  |  |  | // Strings can be in the format of: | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // 	Input  = Type [ "[" [ Number ] "]" ] Name . | 
					
						
							|  |  |  | // 	Type   = [ "u" ] "int" [ Number ] . | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Examples: | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | //      string     int       uint       real | 
					
						
							|  |  |  | //      string32   int8      uint8      uint[] | 
					
						
							|  |  |  | //      address    int256    uint256    real[2] | 
					
						
							|  |  |  | func NewType(t string) (typ Type, err error) { | 
					
						
							|  |  |  | 	// 1. full string 2. type 3. (opt.) is slice 4. (opt.) size | 
					
						
							|  |  |  | 	freg, err := regexp.Compile("([a-zA-Z0-9]+)(\\[([0-9]*)?\\])?") | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return Type{}, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	res := freg.FindAllStringSubmatch(t, -1)[0] | 
					
						
							|  |  |  | 	var ( | 
					
						
							|  |  |  | 		isslice bool | 
					
						
							|  |  |  | 		size    int | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	switch { | 
					
						
							|  |  |  | 	case res[3] != "": | 
					
						
							|  |  |  | 		// err is ignored. Already checked for number through the regexp | 
					
						
							|  |  |  | 		size, _ = strconv.Atoi(res[3]) | 
					
						
							|  |  |  | 		isslice = true | 
					
						
							|  |  |  | 	case res[2] != "": | 
					
						
							|  |  |  | 		isslice = true | 
					
						
							|  |  |  | 		size = -1 | 
					
						
							|  |  |  | 	case res[0] == "": | 
					
						
							|  |  |  | 		return Type{}, fmt.Errorf("type parse error for `%s`", t) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	treg, err := regexp.Compile("([a-zA-Z]+)([0-9]*)?") | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return Type{}, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	parsedType := treg.FindAllStringSubmatch(res[1], -1)[0] | 
					
						
							|  |  |  | 	vsize, _ := strconv.Atoi(parsedType[2]) | 
					
						
							|  |  |  | 	vtype := parsedType[1] | 
					
						
							|  |  |  | 	// substitute canonical representation | 
					
						
							|  |  |  | 	if vsize == 0 && (vtype == "int" || vtype == "uint") { | 
					
						
							|  |  |  | 		vsize = 256 | 
					
						
							|  |  |  | 		t += "256" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if isslice { | 
					
						
							|  |  |  | 		typ.Kind = reflect.Slice | 
					
						
							|  |  |  | 		typ.Size = size | 
					
						
							|  |  |  | 		switch vtype { | 
					
						
							|  |  |  | 		case "int": | 
					
						
							|  |  |  | 			typ.Type = big_ts | 
					
						
							|  |  |  | 		case "uint": | 
					
						
							|  |  |  | 			typ.Type = ubig_ts | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			return Type{}, fmt.Errorf("unsupported arg slice type: %s", t) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		switch vtype { | 
					
						
							|  |  |  | 		case "int": | 
					
						
							|  |  |  | 			typ.Kind = reflect.Ptr | 
					
						
							|  |  |  | 			typ.Type = big_t | 
					
						
							|  |  |  | 			typ.Size = 256 | 
					
						
							|  |  |  | 			typ.T = IntTy | 
					
						
							|  |  |  | 		case "uint": | 
					
						
							|  |  |  | 			typ.Kind = reflect.Ptr | 
					
						
							|  |  |  | 			typ.Type = ubig_t | 
					
						
							|  |  |  | 			typ.Size = 256 | 
					
						
							|  |  |  | 			typ.T = UintTy | 
					
						
							|  |  |  | 		case "bool": | 
					
						
							|  |  |  | 			typ.Kind = reflect.Bool | 
					
						
							| 
									
										
										
										
											2016-02-09 13:57:00 +01:00
										 |  |  | 			typ.T = BoolTy | 
					
						
							| 
									
										
										
										
											2015-01-27 16:19:21 +01:00
										 |  |  | 		case "real": // TODO | 
					
						
							|  |  |  | 			typ.Kind = reflect.Invalid | 
					
						
							|  |  |  | 		case "address": | 
					
						
							|  |  |  | 			typ.Kind = reflect.Slice | 
					
						
							| 
									
										
										
										
											2015-11-20 13:45:37 +01:00
										 |  |  | 			typ.Type = address_t | 
					
						
							| 
									
										
										
										
											2015-01-27 16:19:21 +01:00
										 |  |  | 			typ.Size = 20 | 
					
						
							|  |  |  | 			typ.T = AddressTy | 
					
						
							|  |  |  | 		case "string": | 
					
						
							|  |  |  | 			typ.Kind = reflect.String | 
					
						
							|  |  |  | 			typ.Size = -1 | 
					
						
							| 
									
										
										
										
											2016-02-09 13:57:00 +01:00
										 |  |  | 			typ.T = StringTy | 
					
						
							| 
									
										
										
										
											2015-01-27 16:19:21 +01:00
										 |  |  | 			if vsize > 0 { | 
					
						
							|  |  |  | 				typ.Size = 32 | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-11-20 13:45:37 +01:00
										 |  |  | 		case "hash": | 
					
						
							|  |  |  | 			typ.Kind = reflect.Slice | 
					
						
							|  |  |  | 			typ.Size = 32 | 
					
						
							|  |  |  | 			typ.Type = hash_t | 
					
						
							|  |  |  | 			typ.T = HashTy | 
					
						
							| 
									
										
										
										
											2015-10-28 22:57:21 +01:00
										 |  |  | 		case "bytes": | 
					
						
							|  |  |  | 			typ.Kind = reflect.Slice | 
					
						
							|  |  |  | 			typ.Type = byte_ts | 
					
						
							|  |  |  | 			typ.Size = vsize | 
					
						
							| 
									
										
										
										
											2016-02-09 13:57:00 +01:00
										 |  |  | 			if vsize == 0 { | 
					
						
							|  |  |  | 				typ.T = BytesTy | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				typ.T = FixedBytesTy | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-01-27 16:19:21 +01:00
										 |  |  | 		default: | 
					
						
							|  |  |  | 			return Type{}, fmt.Errorf("unsupported arg type: %s", t) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	typ.stringKind = t | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (t Type) String() (out string) { | 
					
						
							|  |  |  | 	return t.stringKind | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Test the given input parameter `v` and checks if it matches certain | 
					
						
							|  |  |  | // criteria | 
					
						
							|  |  |  | // * Big integers are checks for ptr types and if the given value is | 
					
						
							|  |  |  | //   assignable | 
					
						
							|  |  |  | // * Integer are checked for size | 
					
						
							|  |  |  | // * Strings, addresses and bytes are checks for type and size | 
					
						
							|  |  |  | func (t Type) pack(v interface{}) ([]byte, error) { | 
					
						
							|  |  |  | 	value := reflect.ValueOf(v) | 
					
						
							|  |  |  | 	switch kind := value.Kind(); kind { | 
					
						
							|  |  |  | 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | 
					
						
							|  |  |  | 		if t.Type != ubig_t { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return packNum(value, t.T), nil | 
					
						
							|  |  |  | 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | 
					
						
							|  |  |  | 		if t.Type != ubig_t { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return packNum(value, t.T), nil | 
					
						
							|  |  |  | 	case reflect.Ptr: | 
					
						
							|  |  |  | 		// If the value is a ptr do a assign check (only used by | 
					
						
							|  |  |  | 		// big.Int for now) | 
					
						
							|  |  |  | 		if t.Type == ubig_t && value.Type() != ubig_t { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return packNum(value, t.T), nil | 
					
						
							|  |  |  | 	case reflect.String: | 
					
						
							|  |  |  | 		if t.Size > -1 && value.Len() > t.Size { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("%v out of bound. %d for %d", value.Kind(), value.Len(), t.Size) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-03-16 11:27:38 +01:00
										 |  |  | 		return []byte(common.LeftPadString(t.String(), 32)), nil | 
					
						
							| 
									
										
										
										
											2015-01-27 16:19:21 +01:00
										 |  |  | 	case reflect.Slice: | 
					
						
							|  |  |  | 		if t.Size > -1 && value.Len() > t.Size { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("%v out of bound. %d for %d", value.Kind(), value.Len(), t.Size) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Address is a special slice. The slice acts as one rather than a list of elements. | 
					
						
							|  |  |  | 		if t.T == AddressTy { | 
					
						
							| 
									
										
										
										
											2015-03-16 11:27:38 +01:00
										 |  |  | 			return common.LeftPadBytes(v.([]byte), 32), nil | 
					
						
							| 
									
										
										
										
											2015-01-27 16:19:21 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Signed / Unsigned check | 
					
						
							|  |  |  | 		if (t.T != IntTy && isSigned(value)) || (t.T == UintTy && isSigned(value)) { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("slice of incompatible types.") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		var packed []byte | 
					
						
							|  |  |  | 		for i := 0; i < value.Len(); i++ { | 
					
						
							|  |  |  | 			packed = append(packed, packNum(value.Index(i), t.T)...) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return packed, nil | 
					
						
							|  |  |  | 	case reflect.Bool: | 
					
						
							|  |  |  | 		if value.Bool() { | 
					
						
							| 
									
										
										
										
											2015-03-16 11:27:38 +01:00
										 |  |  | 			return common.LeftPadBytes(common.Big1.Bytes(), 32), nil | 
					
						
							| 
									
										
										
										
											2015-01-27 16:19:21 +01:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2015-03-16 11:27:38 +01:00
										 |  |  | 			return common.LeftPadBytes(common.Big0.Bytes(), 32), nil | 
					
						
							| 
									
										
										
										
											2015-01-27 16:19:21 +01:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-10-28 22:57:21 +01:00
										 |  |  | 	case reflect.Array: | 
					
						
							|  |  |  | 		if v, ok := value.Interface().(common.Address); ok { | 
					
						
							| 
									
										
										
										
											2015-11-20 13:45:37 +01:00
										 |  |  | 			return common.LeftPadBytes(v[:], 32), nil | 
					
						
							| 
									
										
										
										
											2015-10-28 22:57:21 +01:00
										 |  |  | 		} else if v, ok := value.Interface().(common.Hash); ok { | 
					
						
							| 
									
										
										
										
											2015-11-20 13:45:37 +01:00
										 |  |  | 			return v[:], nil | 
					
						
							| 
									
										
										
										
											2015-10-28 22:57:21 +01:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-01-27 16:19:21 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-27 08:38:53 +01:00
										 |  |  | 	return nil, fmt.Errorf("ABI: bad input given %v", value.Kind()) | 
					
						
							| 
									
										
										
										
											2015-01-27 16:19:21 +01:00
										 |  |  | } |