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 }