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

     1  package native
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"runtime"
     7  	"strings"
     8  
     9  	"github.com/hyperledger/burrow/acm"
    10  	"github.com/hyperledger/burrow/crypto"
    11  	"github.com/hyperledger/burrow/execution/engine"
    12  	"github.com/hyperledger/burrow/execution/errors"
    13  	"github.com/hyperledger/burrow/execution/evm/abi"
    14  	"github.com/hyperledger/burrow/logging"
    15  	"github.com/hyperledger/burrow/permission"
    16  )
    17  
    18  // Function is metadata for native functions. Act as call targets
    19  // for the EVM when collected into an Contract. Can be used to generate
    20  // bindings in a smart contract languages.
    21  type Function struct {
    22  	// Comment describing function's purpose, parameters, and return value
    23  	Comment string
    24  	// Permissions required to call function
    25  	PermFlag permission.PermFlag
    26  	// Whether this function writes to state
    27  	Pure bool
    28  	// Native function to which calls will be dispatched when a containing
    29  	F interface{}
    30  	// Following fields are for only for memoization
    31  	// The name of the contract to which this function belongs (if any)
    32  	contractName string
    33  	// Function name (used to form signature)
    34  	name string
    35  	// The abi
    36  	abi *abi.FunctionSpec
    37  	// Address of containing contract
    38  	address   crypto.Address
    39  	externals engine.Dispatcher
    40  	logger    *logging.Logger
    41  }
    42  
    43  var _ engine.Native = &Function{}
    44  
    45  // Context is the first argument to any native function. This struct carries
    46  // all the context an native needs to access e.g. state in burrow.
    47  type Context struct {
    48  	State engine.State
    49  	engine.CallParams
    50  	// TODO: this allows us to call back to EVM contracts if we wish - make use of it somewhere...
    51  	externals engine.Dispatcher
    52  	Logger    *logging.Logger
    53  }
    54  
    55  // Created a new function mounted directly at address (i.e. no Solidity contract or function selection)
    56  func NewFunction(comment string, address crypto.Address, permFlag permission.PermFlag, f interface{}) (*Function, error) {
    57  	function := &Function{
    58  		Comment:  comment,
    59  		PermFlag: permFlag,
    60  		F:        f,
    61  	}
    62  	err := function.init(address)
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  	return function, nil
    67  }
    68  
    69  func (f *Function) SetExternals(externals engine.Dispatcher) {
    70  	// Wrap it to treat nil dispatcher as empty list
    71  	f.externals = engine.NewDispatchers(externals)
    72  }
    73  
    74  func (f *Function) Call(state engine.State, params engine.CallParams) ([]byte, error) {
    75  	return engine.Call(state, params, f.execute)
    76  }
    77  
    78  func (f *Function) execute(state engine.State, params engine.CallParams) ([]byte, error) {
    79  	// check if we have permission to call this function
    80  	hasPermission, err := engine.HasPermission(state.CallFrame, params.Caller, f.PermFlag)
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  	if !hasPermission {
    85  		return nil, &errors.LacksNativePermission{Address: params.Caller, NativeName: f.name}
    86  	}
    87  
    88  	ctx := Context{
    89  		State:      state,
    90  		CallParams: params,
    91  		externals:  f.externals,
    92  		Logger:     f.logger,
    93  	}
    94  	fnv := reflect.ValueOf(f.F)
    95  	fnt := fnv.Type()
    96  
    97  	args := []reflect.Value{reflect.ValueOf(ctx)}
    98  
    99  	if f.abi != nil {
   100  		arguments := reflect.New(fnt.In(1))
   101  		err = abi.Unpack(f.abi.Inputs, params.Input, arguments.Interface())
   102  		if err != nil {
   103  			return nil, err
   104  		}
   105  		args = append(args, arguments.Elem())
   106  	}
   107  
   108  	rets := fnv.Call(args)
   109  	if !rets[1].IsNil() {
   110  		return nil, rets[1].Interface().(error)
   111  	}
   112  
   113  	ret := rets[0].Interface()
   114  	if f.abi != nil {
   115  		return abi.Pack(f.abi.Outputs, ret)
   116  	}
   117  
   118  	output, ok := ret.([]byte)
   119  	if !ok {
   120  		return nil, fmt.Errorf("function has no associated ABI but returns %T instead of []byte", ret)
   121  	}
   122  	return output, nil
   123  }
   124  
   125  func (f *Function) FullName() string {
   126  	if f.contractName != "" {
   127  		return f.contractName + "." + f.name
   128  	}
   129  	return f.name
   130  }
   131  
   132  func (f *Function) Address() crypto.Address {
   133  	return f.address
   134  }
   135  
   136  // Signature returns the function signature as would be used for ABI hashing
   137  func (f *Function) Signature() string {
   138  	argTypeNames := make([]string, len(f.abi.Inputs))
   139  	for i, arg := range f.abi.Inputs {
   140  		argTypeNames[i] = arg.EVM.GetSignature()
   141  	}
   142  	return fmt.Sprintf("%s(%s)", f.name, strings.Join(argTypeNames, ","))
   143  }
   144  
   145  // For templates
   146  func (f *Function) Name() string {
   147  	return f.name
   148  }
   149  
   150  // NArgs returns the number of function arguments
   151  func (f *Function) NArgs() int {
   152  	return len(f.abi.Inputs)
   153  }
   154  
   155  // Abi returns the FunctionSpec for this function
   156  func (f *Function) Abi() *abi.FunctionSpec {
   157  	return f.abi
   158  }
   159  
   160  func (f *Function) ContractMeta() []*acm.ContractMeta {
   161  	// FIXME: can we do something here - a function is not a contract...
   162  	return nil
   163  }
   164  
   165  func (f *Function) String() string {
   166  	return fmt.Sprintf("SNativeFunction{Name: %s; Inputs: %d; Outputs: %d}",
   167  		f.name, len(f.abi.Inputs), len(f.abi.Outputs))
   168  }
   169  
   170  func (f *Function) init(address crypto.Address) error {
   171  	// Get name of function
   172  	t := reflect.TypeOf(f.F)
   173  	v := reflect.ValueOf(f.F)
   174  	// v.String() for functions returns the empty string
   175  	fullyQualifiedName := runtime.FuncForPC(v.Pointer()).Name()
   176  	a := strings.Split(fullyQualifiedName, ".")
   177  	f.name = a[len(a)-1]
   178  
   179  	if t.NumIn() != 1 && t.NumIn() != 2 {
   180  		return fmt.Errorf("native function %s must have a one or two arguments", fullyQualifiedName)
   181  	}
   182  
   183  	if t.NumOut() != 2 {
   184  		return fmt.Errorf("native function %s must return a single struct and an error", fullyQualifiedName)
   185  	}
   186  
   187  	if t.In(0) != reflect.TypeOf(Context{}) {
   188  		return fmt.Errorf("first agument of %s must be struct Context", fullyQualifiedName)
   189  	}
   190  
   191  	if t.NumIn() == 2 {
   192  		f.abi = abi.SpecFromStructReflect(f.name, t.In(1), t.Out(0))
   193  	}
   194  	f.address = address
   195  	return nil
   196  }