github.com/datachainlab/burrow@v0.25.0/execution/evm/abi/core.go (about)

     1  package abi
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path"
     7  	"path/filepath"
     8  
     9  	"github.com/hyperledger/burrow/deploy/compile"
    10  	"github.com/hyperledger/burrow/execution/errors"
    11  	"github.com/hyperledger/burrow/logging"
    12  )
    13  
    14  // Variable exist to unpack return values into, so have both the return
    15  // value and its name
    16  type Variable struct {
    17  	Name  string
    18  	Value string
    19  }
    20  
    21  func init() {
    22  	var err error
    23  	RevertAbi, err = ReadAbiSpec([]byte(`[{"name":"Error","type":"function","outputs":[{"type":"string"}],"inputs":[{"type":"string"}]}]`))
    24  	if err != nil {
    25  		panic(fmt.Sprintf("internal error: failed to build revert abi: %v", err))
    26  	}
    27  }
    28  
    29  // RevertAbi exists to decode reverts. Any contract function call fail using revert(), assert() or require().
    30  // If a function exits this way, the this hardcoded ABI will be used.
    31  var RevertAbi *AbiSpec
    32  
    33  // EncodeFunctionCallFromFile ABI encodes a function call based on ABI in file, and the
    34  // arguments specified as strings.
    35  // The abiFileName specifies the name of the ABI file, and abiPath the path where it can be found.
    36  // The fname specifies which function should called, if
    37  // it doesn't exist exist the fallback function will be called. If fname is the empty
    38  // string, the constructor is called. The arguments must be specified in args. The count
    39  // must match the function being called.
    40  // Returns the ABI encoded function call, whether the function is constant according
    41  // to the ABI (which means it does not modified contract state)
    42  func EncodeFunctionCallFromFile(abiFileName, abiPath, funcName string, logger *logging.Logger, args ...interface{}) ([]byte, *FunctionSpec, error) {
    43  	abiSpecBytes, err := readAbi(abiPath, abiFileName, logger)
    44  	if err != nil {
    45  		return []byte{}, nil, err
    46  	}
    47  
    48  	return EncodeFunctionCall(abiSpecBytes, funcName, logger, args...)
    49  }
    50  
    51  // EncodeFunctionCall ABI encodes a function call based on ABI in string abiData
    52  // and the arguments specified as strings.
    53  // The fname specifies which function should called, if
    54  // it doesn't exist exist the fallback function will be called. If fname is the empty
    55  // string, the constructor is called. The arguments must be specified in args. The count
    56  // must match the function being called.
    57  // Returns the ABI encoded function call, whether the function is constant according
    58  // to the ABI (which means it does not modified contract state)
    59  func EncodeFunctionCall(abiData, funcName string, logger *logging.Logger, args ...interface{}) ([]byte, *FunctionSpec, error) {
    60  	logger.TraceMsg("Packing Call via ABI",
    61  		"spec", abiData,
    62  		"function", funcName,
    63  		"arguments", fmt.Sprintf("%v", args),
    64  	)
    65  
    66  	abiSpec, err := ReadAbiSpec([]byte(abiData))
    67  	if err != nil {
    68  		logger.InfoMsg("Failed to decode abi spec",
    69  			"abi", abiData,
    70  			"error", err.Error(),
    71  		)
    72  		return nil, nil, err
    73  	}
    74  
    75  	packedBytes, funcSpec, err := abiSpec.Pack(funcName, args...)
    76  	if err != nil {
    77  		logger.InfoMsg("Failed to encode abi spec",
    78  			"abi", abiData,
    79  			"error", err.Error(),
    80  		)
    81  		return nil, nil, err
    82  	}
    83  
    84  	return packedBytes, funcSpec, nil
    85  }
    86  
    87  // DecodeFunctionReturnFromFile ABI decodes the return value from a contract function call.
    88  func DecodeFunctionReturnFromFile(abiLocation, binPath, funcName string, resultRaw []byte, logger *logging.Logger) ([]*Variable, error) {
    89  	abiSpecBytes, err := readAbi(binPath, abiLocation, logger)
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  	logger.TraceMsg("ABI Specification (Decode)", "spec", abiSpecBytes)
    94  
    95  	// Unpack the result
    96  	return DecodeFunctionReturn(abiSpecBytes, funcName, resultRaw)
    97  }
    98  
    99  func DecodeFunctionReturn(abiData, name string, data []byte) ([]*Variable, error) {
   100  	abiSpec, err := ReadAbiSpec([]byte(abiData))
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  
   105  	var args []Argument
   106  
   107  	if name == "" {
   108  		args = abiSpec.Constructor.Outputs
   109  	} else {
   110  		if _, ok := abiSpec.Functions[name]; ok {
   111  			args = abiSpec.Functions[name].Outputs
   112  		} else {
   113  			args = abiSpec.Fallback.Outputs
   114  		}
   115  	}
   116  
   117  	if args == nil {
   118  		return nil, fmt.Errorf("no such function")
   119  	}
   120  	vars := make([]*Variable, len(args))
   121  
   122  	if len(args) == 0 {
   123  		return nil, nil
   124  	}
   125  
   126  	vals := make([]interface{}, len(args))
   127  	for i := range vals {
   128  		vals[i] = new(string)
   129  	}
   130  	err = Unpack(args, data, vals...)
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  
   135  	for i, a := range args {
   136  		if a.Name != "" {
   137  			vars[i] = &Variable{Name: a.Name, Value: *(vals[i].(*string))}
   138  		} else {
   139  			vars[i] = &Variable{Name: fmt.Sprintf("%d", i), Value: *(vals[i].(*string))}
   140  		}
   141  	}
   142  
   143  	return vars, nil
   144  }
   145  
   146  func readAbi(root, contract string, logger *logging.Logger) (string, error) {
   147  	p := path.Join(root, stripHex(contract))
   148  	if _, err := os.Stat(p); err != nil {
   149  		logger.TraceMsg("abifile not found", "tried", p)
   150  		p = path.Join(root, stripHex(contract)+".bin")
   151  		if _, err = os.Stat(p); err != nil {
   152  			logger.TraceMsg("abifile not found", "tried", p)
   153  			return "", fmt.Errorf("Abi doesn't exist for =>\t%s", p)
   154  		}
   155  	}
   156  	logger.TraceMsg("Found ABI file", "path", p)
   157  	sol, err := compile.LoadSolidityContract(p)
   158  	if err != nil {
   159  		return "", err
   160  	}
   161  	return string(sol.Abi), nil
   162  }
   163  
   164  // LoadPath loads one abi file or finds all files in a directory
   165  func LoadPath(abiFileOrDirs ...string) (*AbiSpec, error) {
   166  	if len(abiFileOrDirs) == 0 {
   167  		return &AbiSpec{}, fmt.Errorf("no ABI file or directory provided")
   168  	}
   169  
   170  	specs := make([]*AbiSpec, 0)
   171  
   172  	for _, dir := range abiFileOrDirs {
   173  		err := filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
   174  			if err != nil {
   175  				return fmt.Errorf("error returned while walking abiDir '%s': %v", dir, err)
   176  			}
   177  			ext := filepath.Ext(path)
   178  			if fi.IsDir() || !(ext == ".bin" || ext == ".abi") {
   179  				return nil
   180  			}
   181  			if err == nil {
   182  				abiSpc, err := ReadAbiSpecFile(path)
   183  				if err != nil {
   184  					return errors.Wrap(err, "Error parsing abi file "+path)
   185  				}
   186  				specs = append(specs, abiSpc)
   187  			}
   188  			return nil
   189  		})
   190  		if err != nil {
   191  			return &AbiSpec{}, err
   192  		}
   193  	}
   194  	return MergeAbiSpec(specs), nil
   195  }
   196  
   197  func stripHex(s string) string {
   198  	if len(s) > 1 {
   199  		if s[:2] == "0x" {
   200  			s = s[2:]
   201  			if len(s)%2 != 0 {
   202  				s = "0" + s
   203  			}
   204  			return s
   205  		}
   206  	}
   207  	return s
   208  }