-
Notifications
You must be signed in to change notification settings - Fork 314
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
524 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,258 @@ | ||
// EVMC: Ethereum Client-VM Connector API. | ||
// Copyright 2018 Pawel Bylica. | ||
// Licensed under the MIT License. See the LICENSE file. | ||
|
||
package evmc | ||
|
||
/* | ||
#cgo CFLAGS: -I${SRCDIR}/../../../include | ||
#cgo LDFLAGS: -ldl | ||
#include <evmc/evmc.h> | ||
#include <evmc/helpers.h> | ||
#include <evmc/loader.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
inline int set_option(struct evmc_instance* instance, _GoString_ name, _GoString_ value) | ||
{ | ||
if (instance->set_option) | ||
return instance->set_option(instance, name.p, value.p); | ||
return -1; | ||
} | ||
struct extended_context | ||
{ | ||
struct evmc_context context; | ||
int64_t index; | ||
}; | ||
extern const struct evmc_context_fn_table fn_table; | ||
static struct evmc_result execute_wrapper(struct evmc_instance* instance, | ||
int64_t context_index, | ||
enum evmc_revision rev, | ||
_GoBytes_ destination, | ||
_GoBytes_ sender, | ||
_GoBytes_ value, | ||
_GoBytes_ input, | ||
_GoBytes_ code_hash, | ||
int64_t gas, | ||
int32_t depth, | ||
uint32_t flags, | ||
_GoBytes_ code) | ||
{ | ||
struct evmc_message msg; | ||
msg.input_data = input.p; | ||
msg.input_size = input.n; | ||
msg.gas = gas; | ||
msg.depth = depth; | ||
msg.kind = EVMC_CALL; | ||
msg.flags = flags; | ||
memcpy(msg.destination.bytes, destination.p, sizeof(msg.destination)); | ||
memcpy(msg.sender.bytes, sender.p, sizeof(msg.sender)); | ||
memcpy(msg.value.bytes, value.p, sizeof(msg.value)); | ||
memcpy(msg.code_hash.bytes, code_hash.p, sizeof(msg.code_hash)); | ||
struct extended_context ctx = {{&fn_table}, context_index}; | ||
return instance->execute(instance, &ctx.context, rev, &msg, code.p, code.n); | ||
} | ||
*/ | ||
import "C" | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"sync" | ||
"unsafe" | ||
|
||
"github.com/ethereum/go-ethereum/common" | ||
) | ||
|
||
type Error int32 | ||
|
||
func (err Error) IsInternalError() bool { | ||
return err < 0 | ||
} | ||
|
||
func (err Error) Error() string { | ||
code := C.enum_evmc_status_code(err) | ||
|
||
switch code { | ||
case C.EVMC_FAILURE: | ||
return "evmc: failure" | ||
case C.EVMC_REVERT: | ||
return "evmc: revert" | ||
case C.EVMC_OUT_OF_GAS: | ||
return "evmc: out of gas" | ||
case C.EVMC_INVALID_INSTRUCTION: | ||
return "evmc: invalid instruction" | ||
case C.EVMC_UNDEFINED_INSTRUCTION: | ||
return "evmc: undefined instruction" | ||
case C.EVMC_STACK_OVERFLOW: | ||
return "evmc: stack overflow" | ||
case C.EVMC_STACK_UNDERFLOW: | ||
return "evmc: stack underflow" | ||
case C.EVMC_BAD_JUMP_DESTINATION: | ||
return "evmc: bad jump destination" | ||
case C.EVMC_INVALID_MEMORY_ACCESS: | ||
return "evmc: invalid memory access" | ||
case C.EVMC_CALL_DEPTH_EXCEEDED: | ||
return "evmc: call depth exceeded" | ||
case C.EVMC_STATIC_MODE_VIOLATION: | ||
return "evmc: static mode violation" | ||
case C.EVMC_PRECOMPILE_FAILURE: | ||
return "evmc: precompile failure" | ||
case C.EVMC_CONTRACT_VALIDATION_FAILURE: | ||
return "evmc: contract validation failure" | ||
case C.EVMC_INTERNAL_ERROR: | ||
return "evmc: internal error" | ||
case C.EVMC_REJECTED: | ||
return "evmc: rejected" | ||
} | ||
|
||
if code < 0 { | ||
return fmt.Sprintf("evmc: unknown internal error (%d)", int32(code)) | ||
} | ||
|
||
panic(fmt.Sprintf("evmc: unknown status code %d", int32(code))) | ||
} | ||
|
||
const ( | ||
Failure = Error(C.EVMC_FAILURE) | ||
Revert = Error(C.EVMC_REVERT) | ||
) | ||
|
||
type Revision int32 | ||
|
||
const ( | ||
Frontier = C.EVMC_FRONTIER | ||
Homestead = C.EVMC_HOMESTEAD | ||
TangerineWhistle = C.EVMC_TANGERINE_WHISTLE | ||
SpuriousDragon = C.EVMC_SPURIOUS_DRAGON | ||
Byzantium = C.EVMC_BYZANTIUM | ||
Constantinople = C.EVMC_CONSTANTINOPLE | ||
) | ||
|
||
type Instance struct { | ||
handle *C.struct_evmc_instance | ||
} | ||
|
||
func Load(filename string) (instance *Instance, err error) { | ||
cfilename := C.CString(filename) | ||
var loaderErr C.enum_evmc_loader_error_code | ||
handle := C.evmc_load_and_create(cfilename, &loaderErr) | ||
C.free(unsafe.Pointer(cfilename)) | ||
switch loaderErr { | ||
case C.EVMC_LOADER_SUCCESS: | ||
instance = &Instance{handle} | ||
case C.EVMC_LOADER_CANNOT_OPEN: | ||
err = fmt.Errorf("evmc loader: cannot open %s", filename) | ||
case C.EVMC_LOADER_SYMBOL_NOT_FOUND: | ||
err = fmt.Errorf("evmc loader: the EVMC create function not found in %s", filename) | ||
case C.EVMC_LOADER_INVALID_ARGUMENT: | ||
panic("evmc loader: filename argument is empty") | ||
case C.EVMC_LOADER_INSTANCE_CREATION_FAILURE: | ||
err = errors.New("evmc loader: VM instance creation failure") | ||
case C.EVMC_LOADER_ABI_VERSION_MISMATCH: | ||
err = errors.New("evmc loader: ABI version mismatch") | ||
default: | ||
panic(fmt.Sprintf("evmc loader: unexpected error (%d)", loaderErr)) | ||
} | ||
return instance, err | ||
} | ||
|
||
func (instance *Instance) Destroy() { | ||
C.evmc_destroy(instance.handle) | ||
} | ||
|
||
func (instance *Instance) Name() string { | ||
return C.GoString(instance.handle.name) | ||
} | ||
|
||
func (instance *Instance) Version() string { | ||
return C.GoString(instance.handle.version) | ||
} | ||
|
||
func (instance *Instance) SetOption(name string, value string) (err error) { | ||
r := C.set_option(instance.handle, name+"\x00", value+"\x00") | ||
if r == -1 { | ||
err = errors.New("evmc: VM does not accept any options") | ||
} else if r != 0 { | ||
err = fmt.Errorf("evmc: option '%s' not accepted", name) | ||
} | ||
return err | ||
} | ||
|
||
func (instance *Instance) Execute(ctx HostContext, rev Revision, | ||
destination common.Address, sender common.Address, value common.Hash, input []byte, codeHash common.Hash, gas int64, depth int, static bool, | ||
code []byte) (output []byte, gasLeft int64, err error) { | ||
|
||
flags := C.uint32_t(0) | ||
if static { | ||
flags |= C.EVMC_STATIC | ||
} | ||
|
||
ctxId := addHostContext(ctx) | ||
r := C.execute_wrapper(instance.handle, C.int64_t(ctxId), uint32(rev), destination[:], sender[:], value[:], input, | ||
codeHash[:], C.int64_t(gas), C.int32_t(depth), flags, code) | ||
removeHostContext(ctxId) | ||
|
||
output = C.GoBytes(unsafe.Pointer(r.output_data), C.int(r.output_size)) | ||
gasLeft = int64(r.gas_left) | ||
if r.status_code != C.EVMC_SUCCESS { | ||
err = Error(r.status_code) | ||
} | ||
|
||
if r.release != nil { | ||
C.evmc_release_result(&r) | ||
} | ||
|
||
return output, gasLeft, err | ||
} | ||
|
||
var ( | ||
hostContextCounter int | ||
hostContextMap = map[int]HostContext{} | ||
hostContextMapMu sync.Mutex | ||
) | ||
|
||
func addHostContext(ctx HostContext) int { | ||
hostContextMapMu.Lock() | ||
id := hostContextCounter | ||
hostContextCounter++ | ||
hostContextMap[id] = ctx | ||
hostContextMapMu.Unlock() | ||
return id | ||
} | ||
|
||
func removeHostContext(id int) { | ||
hostContextMapMu.Lock() | ||
delete(hostContextMap, id) | ||
hostContextMapMu.Unlock() | ||
} | ||
|
||
func getHostContext(idx int) HostContext { | ||
hostContextMapMu.Lock() | ||
ctx := hostContextMap[idx] | ||
hostContextMapMu.Unlock() | ||
return ctx | ||
} | ||
|
||
func evmcUint256be(in common.Hash) C.struct_evmc_uint256be { | ||
out := C.struct_evmc_uint256be{} | ||
for i := 0; i < len(in); i++ { | ||
out.bytes[i] = C.uint8_t(in[i]) | ||
} | ||
return out | ||
} | ||
|
||
func evmcAddress(address common.Address) C.struct_evmc_address { | ||
r := C.struct_evmc_address{} | ||
for i := 0; i < len(address); i++ { | ||
r.bytes[i] = C.uint8_t(address[i]) | ||
} | ||
return r | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// EVMC: Ethereum Client-VM Connector API. | ||
// Copyright 2018 Pawel Bylica. | ||
// Licensed under the MIT License. See the LICENSE file. | ||
|
||
package evmc | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
) | ||
|
||
func TestLoad(t *testing.T) { | ||
i, err := Load(os.Getenv("EVMC_PATH")) | ||
if err != nil { | ||
t.Fatal(err.Error()) | ||
} | ||
defer i.Destroy() | ||
if i.Name() != "interpreter" { | ||
t.Fatal("name is not 'interpreter'") | ||
} | ||
if i.Version()[0] != '1' { | ||
t.Fatalf("version is %s", i.Version()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
#include "_cgo_export.h" | ||
|
||
#include <stdlib.h> | ||
|
||
const struct evmc_context_fn_table fn_table = { | ||
(evmc_account_exists_fn) accountExists, | ||
(evmc_get_storage_fn) getStorage, | ||
(evmc_set_storage_fn) setStorage, | ||
(evmc_get_balance_fn) getBalance, | ||
(evmc_get_code_size_fn) getCodeSize, | ||
(evmc_copy_code_fn) copyCode, | ||
(evmc_selfdestruct_fn) selfdestruct, | ||
(evmc_call_fn) call, | ||
(evmc_get_tx_context_fn) getTxContext, | ||
(evmc_get_block_hash_fn) getBlockHash, | ||
(evmc_emit_log_fn) emitLog, | ||
}; | ||
|
||
void free_result_output(const struct evmc_result* result) | ||
{ | ||
free((void*)result->output_data); | ||
} |
Oops, something went wrong.