github.com/karalabe/go-ethereum@v0.8.5/accounts/abi/type.go (about)

     1  package abi
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"regexp"
     7  	"strconv"
     8  
     9  	"github.com/ethereum/go-ethereum/ethutil"
    10  )
    11  
    12  const (
    13  	IntTy byte = iota
    14  	UintTy
    15  	BoolTy
    16  	SliceTy
    17  	AddressTy
    18  	RealTy
    19  )
    20  
    21  // Type is the reflection of the supported argument type
    22  type Type struct {
    23  	Kind       reflect.Kind
    24  	Type       reflect.Type
    25  	Size       int
    26  	T          byte   // Our own type checking
    27  	stringKind string // holds the unparsed string for deriving signatures
    28  }
    29  
    30  // New type returns a fully parsed Type given by the input string or an error if it  can't be parsed.
    31  //
    32  // Strings can be in the format of:
    33  //
    34  // 	Input  = Type [ "[" [ Number ] "]" ] Name .
    35  // 	Type   = [ "u" ] "int" [ Number ] .
    36  //
    37  // Examples:
    38  //
    39  //      string     int       uint       real
    40  //      string32   int8      uint8      uint[]
    41  //      address    int256    uint256    real[2]
    42  func NewType(t string) (typ Type, err error) {
    43  	// 1. full string 2. type 3. (opt.) is slice 4. (opt.) size
    44  	freg, err := regexp.Compile("([a-zA-Z0-9]+)(\\[([0-9]*)?\\])?")
    45  	if err != nil {
    46  		return Type{}, err
    47  	}
    48  	res := freg.FindAllStringSubmatch(t, -1)[0]
    49  	var (
    50  		isslice bool
    51  		size    int
    52  	)
    53  	switch {
    54  	case res[3] != "":
    55  		// err is ignored. Already checked for number through the regexp
    56  		size, _ = strconv.Atoi(res[3])
    57  		isslice = true
    58  	case res[2] != "":
    59  		isslice = true
    60  		size = -1
    61  	case res[0] == "":
    62  		return Type{}, fmt.Errorf("type parse error for `%s`", t)
    63  	}
    64  
    65  	treg, err := regexp.Compile("([a-zA-Z]+)([0-9]*)?")
    66  	if err != nil {
    67  		return Type{}, err
    68  	}
    69  
    70  	parsedType := treg.FindAllStringSubmatch(res[1], -1)[0]
    71  	vsize, _ := strconv.Atoi(parsedType[2])
    72  	vtype := parsedType[1]
    73  	// substitute canonical representation
    74  	if vsize == 0 && (vtype == "int" || vtype == "uint") {
    75  		vsize = 256
    76  		t += "256"
    77  	}
    78  
    79  	if isslice {
    80  		typ.Kind = reflect.Slice
    81  		typ.Size = size
    82  		switch vtype {
    83  		case "int":
    84  			typ.Type = big_ts
    85  		case "uint":
    86  			typ.Type = ubig_ts
    87  		default:
    88  			return Type{}, fmt.Errorf("unsupported arg slice type: %s", t)
    89  		}
    90  	} else {
    91  		switch vtype {
    92  		case "int":
    93  			typ.Kind = reflect.Ptr
    94  			typ.Type = big_t
    95  			typ.Size = 256
    96  			typ.T = IntTy
    97  		case "uint":
    98  			typ.Kind = reflect.Ptr
    99  			typ.Type = ubig_t
   100  			typ.Size = 256
   101  			typ.T = UintTy
   102  		case "bool":
   103  			typ.Kind = reflect.Bool
   104  		case "real": // TODO
   105  			typ.Kind = reflect.Invalid
   106  		case "address":
   107  			typ.Kind = reflect.Slice
   108  			typ.Type = byte_ts
   109  			typ.Size = 20
   110  			typ.T = AddressTy
   111  		case "string":
   112  			typ.Kind = reflect.String
   113  			typ.Size = -1
   114  			if vsize > 0 {
   115  				typ.Size = 32
   116  			}
   117  		default:
   118  			return Type{}, fmt.Errorf("unsupported arg type: %s", t)
   119  		}
   120  	}
   121  	typ.stringKind = t
   122  
   123  	return
   124  }
   125  
   126  func (t Type) String() (out string) {
   127  	return t.stringKind
   128  }
   129  
   130  // Test the given input parameter `v` and checks if it matches certain
   131  // criteria
   132  // * Big integers are checks for ptr types and if the given value is
   133  //   assignable
   134  // * Integer are checked for size
   135  // * Strings, addresses and bytes are checks for type and size
   136  func (t Type) pack(v interface{}) ([]byte, error) {
   137  	value := reflect.ValueOf(v)
   138  	switch kind := value.Kind(); kind {
   139  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   140  		if t.Type != ubig_t {
   141  			return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v)
   142  		}
   143  		return packNum(value, t.T), nil
   144  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   145  		if t.Type != ubig_t {
   146  			return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v)
   147  		}
   148  		return packNum(value, t.T), nil
   149  	case reflect.Ptr:
   150  		// If the value is a ptr do a assign check (only used by
   151  		// big.Int for now)
   152  		if t.Type == ubig_t && value.Type() != ubig_t {
   153  			return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v)
   154  		}
   155  		return packNum(value, t.T), nil
   156  	case reflect.String:
   157  		if t.Size > -1 && value.Len() > t.Size {
   158  			return nil, fmt.Errorf("%v out of bound. %d for %d", value.Kind(), value.Len(), t.Size)
   159  		}
   160  		return []byte(ethutil.LeftPadString(t.String(), 32)), nil
   161  	case reflect.Slice:
   162  		if t.Size > -1 && value.Len() > t.Size {
   163  			return nil, fmt.Errorf("%v out of bound. %d for %d", value.Kind(), value.Len(), t.Size)
   164  		}
   165  
   166  		// Address is a special slice. The slice acts as one rather than a list of elements.
   167  		if t.T == AddressTy {
   168  			return ethutil.LeftPadBytes(v.([]byte), 32), nil
   169  		}
   170  
   171  		// Signed / Unsigned check
   172  		if (t.T != IntTy && isSigned(value)) || (t.T == UintTy && isSigned(value)) {
   173  			return nil, fmt.Errorf("slice of incompatible types.")
   174  		}
   175  
   176  		var packed []byte
   177  		for i := 0; i < value.Len(); i++ {
   178  			packed = append(packed, packNum(value.Index(i), t.T)...)
   179  		}
   180  		return packed, nil
   181  	case reflect.Bool:
   182  		if value.Bool() {
   183  			return ethutil.LeftPadBytes(ethutil.Big1.Bytes(), 32), nil
   184  		} else {
   185  			return ethutil.LeftPadBytes(ethutil.Big0.Bytes(), 32), nil
   186  		}
   187  	}
   188  
   189  	panic("unreached")
   190  }