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