/
evmc.go
291 lines (243 loc) · 7.4 KB
/
evmc.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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
// EVMC: Ethereum Client-VM Connector API.
// Copyright 2018 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.
package evmc
/*
#cgo CFLAGS: -I${SRCDIR}/../../../include -Wall -Wextra
#cgo !windows LDFLAGS: -ldl
#include <evmc/evmc.h>
#include <evmc/helpers.h>
#include <evmc/loader.h>
#include <stdlib.h>
#include <string.h>
static inline enum evmc_set_option_result set_option(struct evmc_vm* vm, char* name, char* value)
{
enum evmc_set_option_result ret = evmc_set_option(vm, name, value);
free(name);
free(value);
return ret;
}
extern const struct evmc_host_interface evmc_go_host;
static struct evmc_result execute_wrapper(struct evmc_vm* vm,
uintptr_t context_index, enum evmc_revision rev,
enum evmc_call_kind kind, uint32_t flags, int32_t depth, int64_t gas,
const evmc_address* recipient, const evmc_address* sender,
const uint8_t* input_data, size_t input_size, const evmc_uint256be* value,
const uint8_t* code, size_t code_size)
{
struct evmc_message msg = {
kind,
flags,
depth,
gas,
*recipient,
*sender,
input_data,
input_size,
*value,
{{0}}, // create2_salt: not required for execution
{{0}}, // code_address: not required for execution
0, // code
0, // code_size
};
struct evmc_host_context* context = (struct evmc_host_context*)context_index;
return evmc_execute(vm, &evmc_go_host, context, rev, &msg, code, code_size);
}
*/
import "C"
import (
"fmt"
"sync"
"unsafe"
)
// Hash represents the 32 bytes of arbitrary data (e.g. the result of Keccak256
// hash). It occasionally is used to represent 256-bit unsigned integer values
// stored in big-endian byte order.
type Hash [32]byte
// Address represents the 160-bit (20 bytes) address of an Ethereum account.
type Address [20]byte
// Static asserts.
const (
// The size of evmc_bytes32 equals the size of Hash.
_ = uint(len(Hash{}) - C.sizeof_evmc_bytes32)
_ = uint(C.sizeof_evmc_bytes32 - len(Hash{}))
// The size of evmc_address equals the size of Address.
_ = uint(len(Address{}) - C.sizeof_evmc_address)
_ = uint(C.sizeof_evmc_address - len(Address{}))
)
type Error int32
func (err Error) IsInternalError() bool {
return err < 0
}
func (err Error) Error() string {
return C.GoString(C.evmc_status_code_to_string(C.enum_evmc_status_code(err)))
}
const (
Failure = Error(C.EVMC_FAILURE)
Revert = Error(C.EVMC_REVERT)
)
type Revision int32
const (
Frontier Revision = C.EVMC_FRONTIER
Homestead Revision = C.EVMC_HOMESTEAD
TangerineWhistle Revision = C.EVMC_TANGERINE_WHISTLE
SpuriousDragon Revision = C.EVMC_SPURIOUS_DRAGON
Byzantium Revision = C.EVMC_BYZANTIUM
Constantinople Revision = C.EVMC_CONSTANTINOPLE
Petersburg Revision = C.EVMC_PETERSBURG
Istanbul Revision = C.EVMC_ISTANBUL
Berlin Revision = C.EVMC_BERLIN
London Revision = C.EVMC_LONDON
Paris Revision = C.EVMC_PARIS
Shanghai Revision = C.EVMC_SHANGHAI
Cancun Revision = C.EVMC_CANCUN
Prague Revision = C.EVMC_PRAGUE
Osaka Revision = C.EVMC_OSAKA
MaxRevision Revision = C.EVMC_MAX_REVISION
LatestStableRevision Revision = C.EVMC_LATEST_STABLE_REVISION
)
type VM struct {
handle *C.struct_evmc_vm
}
func Load(filename string) (vm *VM, err error) {
cfilename := C.CString(filename)
loaderErr := C.enum_evmc_loader_error_code(C.EVMC_LOADER_UNSPECIFIED_ERROR)
handle := C.evmc_load_and_create(cfilename, &loaderErr)
C.free(unsafe.Pointer(cfilename))
if loaderErr == C.EVMC_LOADER_SUCCESS {
vm = &VM{handle}
} else {
errMsg := C.evmc_last_error_msg()
if errMsg != nil {
err = fmt.Errorf("EVMC loading error: %s", C.GoString(errMsg))
} else {
err = fmt.Errorf("EVMC loading error %d", int(loaderErr))
}
}
return vm, err
}
func LoadAndConfigure(config string) (vm *VM, err error) {
cconfig := C.CString(config)
loaderErr := C.enum_evmc_loader_error_code(C.EVMC_LOADER_UNSPECIFIED_ERROR)
handle := C.evmc_load_and_configure(cconfig, &loaderErr)
C.free(unsafe.Pointer(cconfig))
if loaderErr == C.EVMC_LOADER_SUCCESS {
vm = &VM{handle}
} else {
errMsg := C.evmc_last_error_msg()
if errMsg != nil {
err = fmt.Errorf("EVMC loading error: %s", C.GoString(errMsg))
} else {
err = fmt.Errorf("EVMC loading error %d", int(loaderErr))
}
}
return vm, err
}
func (vm *VM) Destroy() {
C.evmc_destroy(vm.handle)
}
func (vm *VM) Name() string {
// TODO: consider using C.evmc_vm_name(vm.handle)
return C.GoString(vm.handle.name)
}
func (vm *VM) Version() string {
// TODO: consider using C.evmc_vm_version(vm.handle)
return C.GoString(vm.handle.version)
}
type Capability uint32
const (
CapabilityEVM1 Capability = C.EVMC_CAPABILITY_EVM1
CapabilityEWASM Capability = C.EVMC_CAPABILITY_EWASM
)
func (vm *VM) HasCapability(capability Capability) bool {
return bool(C.evmc_vm_has_capability(vm.handle, uint32(capability)))
}
func (vm *VM) SetOption(name string, value string) (err error) {
r := C.set_option(vm.handle, C.CString(name), C.CString(value))
switch r {
case C.EVMC_SET_OPTION_INVALID_NAME:
err = fmt.Errorf("evmc: option '%s' not accepted", name)
case C.EVMC_SET_OPTION_INVALID_VALUE:
err = fmt.Errorf("evmc: option '%s' has invalid value", name)
case C.EVMC_SET_OPTION_SUCCESS:
}
return err
}
type Result struct {
Output []byte
GasLeft int64
GasRefund int64
}
func (vm *VM) Execute(ctx HostContext, rev Revision,
kind CallKind, static bool, depth int, gas int64,
recipient Address, sender Address, input []byte, value Hash,
code []byte) (res Result, err error) {
flags := C.uint32_t(0)
if static {
flags |= C.EVMC_STATIC
}
ctxId := addHostContext(ctx)
// FIXME: Clarify passing by pointer vs passing by value.
evmcRecipient := evmcAddress(recipient)
evmcSender := evmcAddress(sender)
evmcValue := evmcBytes32(value)
result := C.execute_wrapper(vm.handle, C.uintptr_t(ctxId), uint32(rev),
C.enum_evmc_call_kind(kind), flags, C.int32_t(depth), C.int64_t(gas),
&evmcRecipient, &evmcSender, bytesPtr(input), C.size_t(len(input)), &evmcValue,
bytesPtr(code), C.size_t(len(code)))
removeHostContext(ctxId)
res.Output = C.GoBytes(unsafe.Pointer(result.output_data), C.int(result.output_size))
res.GasLeft = int64(result.gas_left)
res.GasRefund = int64(result.gas_refund)
if result.status_code != C.EVMC_SUCCESS {
err = Error(result.status_code)
}
if result.release != nil {
C.evmc_release_result(&result)
}
return res, err
}
var (
hostContextCounter uintptr
hostContextMap = map[uintptr]HostContext{}
hostContextMapMu sync.Mutex
)
func addHostContext(ctx HostContext) uintptr {
hostContextMapMu.Lock()
id := hostContextCounter
hostContextCounter++
hostContextMap[id] = ctx
hostContextMapMu.Unlock()
return id
}
func removeHostContext(id uintptr) {
hostContextMapMu.Lock()
delete(hostContextMap, id)
hostContextMapMu.Unlock()
}
func getHostContext(idx uintptr) HostContext {
hostContextMapMu.Lock()
ctx := hostContextMap[idx]
hostContextMapMu.Unlock()
return ctx
}
func evmcBytes32(in Hash) C.evmc_bytes32 {
out := C.evmc_bytes32{}
for i := 0; i < len(in); i++ {
out.bytes[i] = C.uint8_t(in[i])
}
return out
}
func evmcAddress(address Address) C.evmc_address {
r := C.evmc_address{}
for i := 0; i < len(address); i++ {
r.bytes[i] = C.uint8_t(address[i])
}
return r
}
func bytesPtr(bytes []byte) *C.uint8_t {
if len(bytes) == 0 {
return nil
}
return (*C.uint8_t)(unsafe.Pointer(&bytes[0]))
}