github.com/ebakus/go-ebakus@v1.0.5-0.20200520105415-dbccef9ec421/accounts/abi/table.go (about)

     1  // Copyright 2019 The ebakus/go-ebakus Authors
     2  // This file is part of the ebakus/go-ebakus library.
     3  //
     4  // The ebakus/go-ebakus library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The ebakus/go-ebakus library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the ebakus/go-ebakus library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package abi
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"reflect"
    23  )
    24  
    25  // Table represents a go struct and is used so as to convert data passed from solidity.
    26  // Input specifies the required struct fields.
    27  type Table struct {
    28  	Name   string
    29  	Inputs Arguments
    30  }
    31  
    32  // Unpack performs the operation hexdata -> Go format
    33  func (table Table) GetTableInstance() (interface{}, error) {
    34  	var abi2interfaceReflect []reflect.StructField
    35  	for i, arg := range table.Inputs {
    36  		if ToCamelCase(arg.Name) == "" {
    37  			return nil, errors.New("abi: purely anonymous or underscored field is not supported")
    38  		}
    39  
    40  		sf := reflect.StructField{
    41  			Name:  ToCamelCase(arg.Name),
    42  			Type:  arg.Type.Type,
    43  			Index: []int{i},
    44  		}
    45  		abi2interfaceReflect = append(abi2interfaceReflect, sf)
    46  	}
    47  
    48  	st := reflect.StructOf(abi2interfaceReflect)
    49  	so := reflect.New(st)
    50  	return so.Interface(), nil
    51  }
    52  
    53  // Unpack performs the operation hexdata -> Go format
    54  func (table Table) UnpackSingle(v interface{}, field string, data []byte) (interface{}, error) {
    55  	if reflect.Ptr != reflect.ValueOf(v).Kind() {
    56  		return nil, fmt.Errorf("abi: Unpack(non-pointer %T)", v)
    57  	}
    58  
    59  	for _, arg := range table.Inputs {
    60  		if arg.Name != field {
    61  			continue
    62  		}
    63  
    64  		elem := reflect.ValueOf(v).Elem()
    65  
    66  		marshalledValue, err := toGoType(0, arg.Type, data)
    67  		if err != nil {
    68  			return nil, err
    69  		}
    70  
    71  		fieldName := ToCamelCase(field)
    72  		if fieldName == "" {
    73  			return nil, errors.New("abi: purely anonymous or underscored field is not supported")
    74  		}
    75  
    76  		elem.FieldByName(fieldName).Set(reflect.ValueOf(marshalledValue))
    77  
    78  		return marshalledValue, nil
    79  	}
    80  
    81  	return nil, nil
    82  }
    83  
    84  // Pack performs the operation Go format -> Hexdata
    85  func (table Table) Pack(args ...interface{}) ([]byte, error) {
    86  	v := args[0]
    87  
    88  	value := reflect.Indirect(reflect.ValueOf(v))
    89  
    90  	// variable input is the output appended at the end of packed
    91  	// output. This is used for strings and bytes types input.
    92  	var variableInput []byte
    93  
    94  	// input offset is the bytes offset for packed output
    95  	inputOffset := 0
    96  	for _, input := range table.Inputs {
    97  		inputOffset += getTypeSize(input.Type)
    98  	}
    99  
   100  	var ret []byte
   101  	for _, input := range table.Inputs {
   102  		fv := value.FieldByName(input.Name)
   103  		packed, err := input.Type.pack(fv)
   104  		if err != nil {
   105  			return nil, err
   106  		}
   107  		// check for dynamic types
   108  		if isDynamicType(input.Type) {
   109  			// set the offset
   110  			ret = append(ret, packNum(reflect.ValueOf(inputOffset))...)
   111  			// calculate next offset
   112  			inputOffset += len(packed)
   113  			// append to variable input
   114  			variableInput = append(variableInput, packed...)
   115  		} else {
   116  			// append the packed value to the input
   117  			ret = append(ret, packed...)
   118  		}
   119  	}
   120  	// append the variable input at the end of the packed input
   121  	ret = append(ret, variableInput...)
   122  
   123  	return ret, nil
   124  }