github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/execution/evm/abi/abi.go (about)

     1  package abi
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"math/big"
     7  	"os"
     8  	"path"
     9  	"path/filepath"
    10  	"reflect"
    11  
    12  	"github.com/hyperledger/burrow/crypto"
    13  	"github.com/hyperledger/burrow/deploy/compile"
    14  	"github.com/hyperledger/burrow/logging"
    15  )
    16  
    17  // Variable exist to unpack return values into, so have both the return
    18  // value and its name
    19  type Variable struct {
    20  	Name  string
    21  	Value string
    22  }
    23  
    24  // LoadPath loads one abi file or finds all files in a directory
    25  func LoadPath(abiFileOrDirs ...string) (*Spec, error) {
    26  	if len(abiFileOrDirs) == 0 {
    27  		return nil, fmt.Errorf("no ABI file or directory provided")
    28  	}
    29  
    30  	specs := make([]*Spec, 0)
    31  
    32  	for _, dir := range abiFileOrDirs {
    33  		err := filepath.WalkDir(dir, func(path string, dir os.DirEntry, err error) error {
    34  			if err != nil {
    35  				return fmt.Errorf("error returned while walking abiDir '%s': %v", dir, err)
    36  			}
    37  			ext := filepath.Ext(path)
    38  			if dir.IsDir() || !(ext == ".bin" || ext == ".abi" || ext == ".json") {
    39  				return nil
    40  			}
    41  			abiSpc, err := ReadSpecFile(path)
    42  			if err != nil {
    43  				return fmt.Errorf("error parsing abi file at %s: %v", path, err)
    44  			}
    45  			specs = append(specs, abiSpc)
    46  			return nil
    47  		})
    48  		if err != nil {
    49  			return nil, err
    50  		}
    51  	}
    52  	return MergeSpec(specs), nil
    53  }
    54  
    55  // EncodeFunctionCallFromFile ABI encodes a function call based on ABI in file, and the
    56  // arguments specified as strings.
    57  // The abiFileName specifies the name of the ABI file, and abiPath the path where it can be found.
    58  // The fname specifies which function should called, if
    59  // it doesn't exist exist the fallback function will be called. If fname is the empty
    60  // string, the constructor is called. The arguments must be specified in args. The count
    61  // must match the function being called.
    62  // Returns the ABI encoded function call, whether the function is constant according
    63  // to the ABI (which means it does not modified contract state)
    64  func EncodeFunctionCallFromFile(abiFileName, abiPath, funcName string, logger *logging.Logger, args ...interface{}) ([]byte, *FunctionSpec, error) {
    65  	abiSpecBytes, err := readAbi(abiPath, abiFileName, logger)
    66  	if err != nil {
    67  		return []byte{}, nil, err
    68  	}
    69  
    70  	return EncodeFunctionCall(abiSpecBytes, funcName, logger, args...)
    71  }
    72  
    73  // EncodeFunctionCall ABI encodes a function call based on ABI in string abiData
    74  // and the arguments specified as strings.
    75  // The fname specifies which function should called, if
    76  // it doesn't exist exist the fallback function will be called. If fname is the empty
    77  // string, the constructor is called. The arguments must be specified in args. The count
    78  // must match the function being called.
    79  // Returns the ABI encoded function call, whether the function is constant according
    80  // to the ABI (which means it does not modified contract state)
    81  func EncodeFunctionCall(abiData, funcName string, logger *logging.Logger, args ...interface{}) ([]byte, *FunctionSpec, error) {
    82  	logger.TraceMsg("Packing Call via ABI",
    83  		"spec", abiData,
    84  		"function", funcName,
    85  		"arguments", fmt.Sprintf("%v", args),
    86  	)
    87  
    88  	abiSpec, err := ReadSpec([]byte(abiData))
    89  	if err != nil {
    90  		logger.InfoMsg("Failed to decode abi spec",
    91  			"abi", abiData,
    92  			"error", err.Error(),
    93  		)
    94  		return nil, nil, err
    95  	}
    96  
    97  	packedBytes, funcSpec, err := abiSpec.Pack(funcName, args...)
    98  	if err != nil {
    99  		logger.InfoMsg("Failed to encode abi spec",
   100  			"abi", abiData,
   101  			"error", err.Error(),
   102  		)
   103  		return nil, nil, err
   104  	}
   105  
   106  	return packedBytes, funcSpec, nil
   107  }
   108  
   109  // DecodeFunctionReturnFromFile ABI decodes the return value from a contract function call.
   110  func DecodeFunctionReturnFromFile(abiLocation, binPath, funcName string, resultRaw []byte, logger *logging.Logger) ([]*Variable, error) {
   111  	abiSpecBytes, err := readAbi(binPath, abiLocation, logger)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	logger.TraceMsg("ABI Specification (Decode)", "spec", abiSpecBytes)
   116  
   117  	// Unpack the result
   118  	return DecodeFunctionReturn(abiSpecBytes, funcName, resultRaw)
   119  }
   120  
   121  func DecodeFunctionReturn(abiData, name string, data []byte) ([]*Variable, error) {
   122  	abiSpec, err := ReadSpec([]byte(abiData))
   123  	if err != nil {
   124  		return nil, err
   125  	}
   126  
   127  	var args []Argument
   128  
   129  	if name == "" {
   130  		args = abiSpec.Constructor.Outputs
   131  	} else {
   132  		if _, ok := abiSpec.Functions[name]; ok {
   133  			args = abiSpec.Functions[name].Outputs
   134  		} else {
   135  			args = abiSpec.Fallback.Outputs
   136  		}
   137  	}
   138  
   139  	if args == nil {
   140  		return nil, fmt.Errorf("no such function")
   141  	}
   142  	vars := make([]*Variable, len(args))
   143  
   144  	if len(args) == 0 {
   145  		return nil, nil
   146  	}
   147  
   148  	vals := make([]interface{}, len(args))
   149  	for i := range vals {
   150  		vals[i] = new(string)
   151  	}
   152  	err = Unpack(args, data, vals...)
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  
   157  	for i, a := range args {
   158  		if a.Name != "" {
   159  			vars[i] = &Variable{Name: a.Name, Value: *(vals[i].(*string))}
   160  		} else {
   161  			vars[i] = &Variable{Name: fmt.Sprintf("%d", i), Value: *(vals[i].(*string))}
   162  		}
   163  	}
   164  
   165  	return vars, nil
   166  }
   167  
   168  // Spec
   169  
   170  // ReadSpecFile reads an ABI file from a file
   171  func ReadSpecFile(filename string) (*Spec, error) {
   172  	specBytes, err := ioutil.ReadFile(filename)
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  
   177  	return ReadSpec(specBytes)
   178  }
   179  
   180  // Struct reflection
   181  
   182  // SpecFromStructReflect generates a FunctionSpec where the arguments and return values are
   183  // described a struct. Both args and rets should be set to the return value of reflect.TypeOf()
   184  // with the respective struct as an argument.
   185  func SpecFromStructReflect(fname string, args reflect.Type, rets reflect.Type) *FunctionSpec {
   186  	inputs := make([]Argument, args.NumField())
   187  	outputs := make([]Argument, rets.NumField())
   188  
   189  	for i := 0; i < args.NumField(); i++ {
   190  		f := args.Field(i)
   191  		a := typeFromReflect(f.Type)
   192  		a.Name = f.Name
   193  		inputs[i] = a
   194  	}
   195  
   196  	for i := 0; i < rets.NumField(); i++ {
   197  		f := rets.Field(i)
   198  		a := typeFromReflect(f.Type)
   199  		a.Name = f.Name
   200  		outputs[i] = a
   201  	}
   202  
   203  	return NewFunctionSpec(fname, inputs, outputs)
   204  }
   205  
   206  func SpecFromFunctionReflect(fname string, v reflect.Value, skipIn, skipOut int) *FunctionSpec {
   207  	t := v.Type()
   208  
   209  	if t.Kind() != reflect.Func {
   210  		panic(fmt.Sprintf("%s is not a function", t.Name()))
   211  	}
   212  
   213  	inputs := make([]Argument, t.NumIn()-skipIn)
   214  	outputs := make([]Argument, t.NumOut()-skipOut)
   215  
   216  	for i := range inputs {
   217  		inputs[i] = typeFromReflect(t.In(i + skipIn))
   218  	}
   219  
   220  	for i := range outputs {
   221  		outputs[i] = typeFromReflect(t.Out(i))
   222  	}
   223  
   224  	return NewFunctionSpec(fname, inputs, outputs)
   225  }
   226  
   227  func GetPackingTypes(args []Argument) []interface{} {
   228  	res := make([]interface{}, len(args))
   229  
   230  	for i, a := range args {
   231  		if a.IsArray {
   232  			t := reflect.TypeOf(a.EVM.getGoType())
   233  			res[i] = reflect.MakeSlice(reflect.SliceOf(t), int(a.ArrayLength), 0).Interface()
   234  		} else {
   235  			res[i] = a.EVM.getGoType()
   236  		}
   237  	}
   238  
   239  	return res
   240  }
   241  
   242  func typeFromReflect(v reflect.Type) Argument {
   243  	arg := Argument{Name: v.Name()}
   244  
   245  	if v == reflect.TypeOf(crypto.Address{}) {
   246  		arg.EVM = EVMAddress{}
   247  	} else if v == reflect.TypeOf(big.Int{}) {
   248  		arg.EVM = EVMInt{M: 256}
   249  	} else {
   250  		if v.Kind() == reflect.Array {
   251  			arg.IsArray = true
   252  			arg.ArrayLength = uint64(v.Len())
   253  			v = v.Elem()
   254  		} else if v.Kind() == reflect.Slice {
   255  			arg.IsArray = true
   256  			v = v.Elem()
   257  		}
   258  
   259  		switch v.Kind() {
   260  		case reflect.Bool:
   261  			arg.EVM = EVMBool{}
   262  		case reflect.String:
   263  			arg.EVM = EVMString{}
   264  		case reflect.Uint64:
   265  			arg.EVM = EVMUint{M: 64}
   266  		case reflect.Int64:
   267  			arg.EVM = EVMInt{M: 64}
   268  		default:
   269  			panic(fmt.Sprintf("no mapping for type %v", v.Kind()))
   270  		}
   271  	}
   272  
   273  	return arg
   274  }
   275  
   276  func readAbi(root, contract string, logger *logging.Logger) (string, error) {
   277  	p := path.Join(root, stripHex(contract))
   278  	if _, err := os.Stat(p); err != nil {
   279  		logger.TraceMsg("abifile not found", "tried", p)
   280  		p = path.Join(root, stripHex(contract)+".bin")
   281  		if _, err = os.Stat(p); err != nil {
   282  			logger.TraceMsg("abifile not found", "tried", p)
   283  			return "", fmt.Errorf("abi doesn't exist for =>\t%s", p)
   284  		}
   285  	}
   286  	logger.TraceMsg("Found ABI file", "path", p)
   287  	sol, err := compile.LoadSolidityContract(p)
   288  	if err != nil {
   289  		return "", err
   290  	}
   291  	return string(sol.Abi), nil
   292  }
   293  
   294  func stripHex(s string) string {
   295  	if len(s) > 1 {
   296  		if s[:2] == "0x" {
   297  			s = s[2:]
   298  			if len(s)%2 != 0 {
   299  				s = "0" + s
   300  			}
   301  			return s
   302  		}
   303  	}
   304  	return s
   305  }