github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/accounts/abi/argument.go (about)

     1  // This file is part of the go-sberex library. The go-sberex library is 
     2  // free software: you can redistribute it and/or modify it under the terms 
     3  // of the GNU Lesser General Public License as published by the Free 
     4  // Software Foundation, either version 3 of the License, or (at your option)
     5  // any later version.
     6  //
     7  // The go-sberex library is distributed in the hope that it will be useful, 
     8  // but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 
    10  // General Public License <http://www.gnu.org/licenses/> for more details.
    11  
    12  package abi
    13  
    14  import (
    15  	"encoding/json"
    16  	"fmt"
    17  	"reflect"
    18  	"strings"
    19  )
    20  
    21  // Argument holds the name of the argument and the corresponding type.
    22  // Types are used when packing and testing arguments.
    23  type Argument struct {
    24  	Name    string
    25  	Type    Type
    26  	Indexed bool // indexed is only used by events
    27  }
    28  
    29  type Arguments []Argument
    30  
    31  // UnmarshalJSON implements json.Unmarshaler interface
    32  func (argument *Argument) UnmarshalJSON(data []byte) error {
    33  	var extarg struct {
    34  		Name    string
    35  		Type    string
    36  		Indexed bool
    37  	}
    38  	err := json.Unmarshal(data, &extarg)
    39  	if err != nil {
    40  		return fmt.Errorf("argument json err: %v", err)
    41  	}
    42  
    43  	argument.Type, err = NewType(extarg.Type)
    44  	if err != nil {
    45  		return err
    46  	}
    47  	argument.Name = extarg.Name
    48  	argument.Indexed = extarg.Indexed
    49  
    50  	return nil
    51  }
    52  
    53  // LengthNonIndexed returns the number of arguments when not counting 'indexed' ones. Only events
    54  // can ever have 'indexed' arguments, it should always be false on arguments for method input/output
    55  func (arguments Arguments) LengthNonIndexed() int {
    56  	out := 0
    57  	for _, arg := range arguments {
    58  		if !arg.Indexed {
    59  			out++
    60  		}
    61  	}
    62  	return out
    63  }
    64  
    65  // NonIndexed returns the arguments with indexed arguments filtered out
    66  func (arguments Arguments) NonIndexed() Arguments {
    67  	var ret []Argument
    68  	for _, arg := range arguments {
    69  		if !arg.Indexed {
    70  			ret = append(ret, arg)
    71  		}
    72  	}
    73  	return ret
    74  }
    75  
    76  // isTuple returns true for non-atomic constructs, like (uint,uint) or uint[]
    77  func (arguments Arguments) isTuple() bool {
    78  	return len(arguments) > 1
    79  }
    80  
    81  // Unpack performs the operation hexdata -> Go format
    82  func (arguments Arguments) Unpack(v interface{}, data []byte) error {
    83  
    84  	// make sure the passed value is arguments pointer
    85  	if reflect.Ptr != reflect.ValueOf(v).Kind() {
    86  		return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
    87  	}
    88  	marshalledValues, err := arguments.UnpackValues(data)
    89  	if err != nil {
    90  		return err
    91  	}
    92  	if arguments.isTuple() {
    93  		return arguments.unpackTuple(v, marshalledValues)
    94  	}
    95  	return arguments.unpackAtomic(v, marshalledValues)
    96  }
    97  
    98  func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interface{}) error {
    99  
   100  	var (
   101  		value = reflect.ValueOf(v).Elem()
   102  		typ   = value.Type()
   103  		kind  = value.Kind()
   104  	)
   105  
   106  	if err := requireUnpackKind(value, typ, kind, arguments); err != nil {
   107  		return err
   108  	}
   109  	// If the output interface is a struct, make sure names don't collide
   110  	if kind == reflect.Struct {
   111  		exists := make(map[string]bool)
   112  		for _, arg := range arguments {
   113  			field := capitalise(arg.Name)
   114  			if field == "" {
   115  				return fmt.Errorf("abi: purely underscored output cannot unpack to struct")
   116  			}
   117  			if exists[field] {
   118  				return fmt.Errorf("abi: multiple outputs mapping to the same struct field '%s'", field)
   119  			}
   120  			exists[field] = true
   121  		}
   122  	}
   123  	for i, arg := range arguments.NonIndexed() {
   124  
   125  		reflectValue := reflect.ValueOf(marshalledValues[i])
   126  
   127  		switch kind {
   128  		case reflect.Struct:
   129  			name := capitalise(arg.Name)
   130  			for j := 0; j < typ.NumField(); j++ {
   131  				// TODO read tags: `abi:"fieldName"`
   132  				if typ.Field(j).Name == name {
   133  					if err := set(value.Field(j), reflectValue, arg); err != nil {
   134  						return err
   135  					}
   136  				}
   137  			}
   138  		case reflect.Slice, reflect.Array:
   139  			if value.Len() < i {
   140  				return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len())
   141  			}
   142  			v := value.Index(i)
   143  			if err := requireAssignable(v, reflectValue); err != nil {
   144  				return err
   145  			}
   146  
   147  			if err := set(v.Elem(), reflectValue, arg); err != nil {
   148  				return err
   149  			}
   150  		default:
   151  			return fmt.Errorf("abi:[2] cannot unmarshal tuple in to %v", typ)
   152  		}
   153  	}
   154  	return nil
   155  }
   156  
   157  // unpackAtomic unpacks ( hexdata -> go ) a single value
   158  func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues []interface{}) error {
   159  	if len(marshalledValues) != 1 {
   160  		return fmt.Errorf("abi: wrong length, expected single value, got %d", len(marshalledValues))
   161  	}
   162  	elem := reflect.ValueOf(v).Elem()
   163  	reflectValue := reflect.ValueOf(marshalledValues[0])
   164  	return set(elem, reflectValue, arguments.NonIndexed()[0])
   165  }
   166  
   167  // UnpackValues can be used to unpack ABI-encoded hexdata according to the ABI-specification,
   168  // without supplying a struct to unpack into. Instead, this method returns a list containing the
   169  // values. An atomic argument will be a list with one element.
   170  func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
   171  	retval := make([]interface{}, 0, arguments.LengthNonIndexed())
   172  	virtualArgs := 0
   173  	for index, arg := range arguments.NonIndexed() {
   174  		marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data)
   175  		if arg.Type.T == ArrayTy {
   176  			// If we have a static array, like [3]uint256, these are coded as
   177  			// just like uint256,uint256,uint256.
   178  			// This means that we need to add two 'virtual' arguments when
   179  			// we count the index from now on
   180  
   181  			virtualArgs += arg.Type.Size - 1
   182  		}
   183  		if err != nil {
   184  			return nil, err
   185  		}
   186  		retval = append(retval, marshalledValue)
   187  	}
   188  	return retval, nil
   189  }
   190  
   191  // PackValues performs the operation Go format -> Hexdata
   192  // It is the semantic opposite of UnpackValues
   193  func (arguments Arguments) PackValues(args []interface{}) ([]byte, error) {
   194  	return arguments.Pack(args...)
   195  }
   196  
   197  // Pack performs the operation Go format -> Hexdata
   198  func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
   199  	// Make sure arguments match up and pack them
   200  	abiArgs := arguments
   201  	if len(args) != len(abiArgs) {
   202  		return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(abiArgs))
   203  	}
   204  	// variable input is the output appended at the end of packed
   205  	// output. This is used for strings and bytes types input.
   206  	var variableInput []byte
   207  
   208  	// input offset is the bytes offset for packed output
   209  	inputOffset := 0
   210  	for _, abiArg := range abiArgs {
   211  		if abiArg.Type.T == ArrayTy {
   212  			inputOffset += 32 * abiArg.Type.Size
   213  		} else {
   214  			inputOffset += 32
   215  		}
   216  	}
   217  	var ret []byte
   218  	for i, a := range args {
   219  		input := abiArgs[i]
   220  		// pack the input
   221  		packed, err := input.Type.pack(reflect.ValueOf(a))
   222  		if err != nil {
   223  			return nil, err
   224  		}
   225  		// check for a slice type (string, bytes, slice)
   226  		if input.Type.requiresLengthPrefix() {
   227  			// calculate the offset
   228  			offset := inputOffset + len(variableInput)
   229  			// set the offset
   230  			ret = append(ret, packNum(reflect.ValueOf(offset))...)
   231  			// Append the packed output to the variable input. The variable input
   232  			// will be appended at the end of the input.
   233  			variableInput = append(variableInput, packed...)
   234  		} else {
   235  			// append the packed value to the input
   236  			ret = append(ret, packed...)
   237  		}
   238  	}
   239  	// append the variable input at the end of the packed input
   240  	ret = append(ret, variableInput...)
   241  
   242  	return ret, nil
   243  }
   244  
   245  // capitalise makes the first character of a string upper case, also removing any
   246  // prefixing underscores from the variable names.
   247  func capitalise(input string) string {
   248  	for len(input) > 0 && input[0] == '_' {
   249  		input = input[1:]
   250  	}
   251  	if len(input) == 0 {
   252  		return ""
   253  	}
   254  	return strings.ToUpper(input[:1]) + input[1:]
   255  }