mirror of
https://github.com/status-im/evmc.git
synced 2025-02-22 07:58:19 +00:00
Initial implementation of Java bindings
Includes JNI bindings, tests and build system
This commit is contained in:
parent
1de783316a
commit
77f5747a5f
@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning].
|
||||
|
||||
### Added
|
||||
|
||||
- Added Java bindings.
|
||||
[#455](https://github.com/ethereum/evmc/pull/455)
|
||||
- Added `MockedHost` C++ class (in form of header-only `evmc::mocked_host` library)
|
||||
which can be used to emulate Host behavior when testing VM implementations.
|
||||
[#456](https://github.com/ethereum/evmc/pull/456)
|
||||
|
@ -25,6 +25,7 @@ Please visit the [documentation].
|
||||
| **C++** | C++11, C++14, C++17 | GCC 6+, clang 3.8+, MSVC 2015+
|
||||
| **Go** _(bindings)_ | 1.9 - 1.12 |
|
||||
| **Rust** _(bindings)_[¹](#n1) | 2018 edition | 1.37.0 and newer
|
||||
| **Java** _(bindings)_ | 11 |
|
||||
|
||||
<b id="n1">1</b>. Rust support is limited and not complete yet, but it is mostly functional already. Breaking changes are possible at this stage.
|
||||
|
||||
|
23
bindings/java/.gitignore
vendored
Normal file
23
bindings/java/.gitignore
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
.idea/
|
||||
.vscode
|
||||
java/build/*
|
||||
java/out/*
|
||||
c/build/*
|
||||
*.o
|
||||
*.dylib
|
||||
.DS_Store
|
||||
.gradle
|
||||
build
|
||||
.classpath
|
||||
.project
|
||||
.settings/
|
||||
java/.classpath
|
||||
java/.project
|
||||
java/.settings/
|
||||
java/bin/
|
||||
java/hs_err_pid*
|
||||
gradle/
|
||||
gradlew
|
||||
gradlew.bat
|
||||
c/evmc-vm.h
|
||||
*.class
|
46
bindings/java/Makefile
Normal file
46
bindings/java/Makefile
Normal file
@ -0,0 +1,46 @@
|
||||
OS:=$(shell uname -s | tr '[:upper:]' '[:lower:]')
|
||||
ifeq ($(OS), linux)
|
||||
EXT:=so
|
||||
OS_LFLAGS:=
|
||||
JAVA_HOME:=/usr/local/openjdk-11
|
||||
else ifeq ($(OS), darwin)
|
||||
EXT:=so
|
||||
OS_LFLAGS:=-mmacosx-version-min=$(shell defaults read loginwindow SystemVersionStampAsString) -framework CoreFoundation -framework Security
|
||||
JAVA_HOME:=$(shell java -XshowSettings:properties -version 2>&1 > /dev/null | grep 'java.home' | sed 's/\s*java.home = //' | sed 's/\/jre//')
|
||||
endif
|
||||
|
||||
INCLUDES = -I../../include
|
||||
JAVA_INCLUDES = -I$(JAVA_HOME)/include/$(OS) -I$(JAVA_HOME)/include
|
||||
JAVA_LIBS = -L$(JAVA_HOME)/lib/server -ljvm
|
||||
CFLAGS = -O2 -fPIC
|
||||
LFLAGS = -shared
|
||||
OUT_DIR = ./c/build
|
||||
|
||||
gradlew:
|
||||
gradle setup
|
||||
|
||||
build: gradlew
|
||||
mkdir -p $(OUT_DIR)
|
||||
javac ./java/src/main/java/org/ethereum/evmc/EvmcVm.java -h ./c --class-path ./java/src/main/java -s ./java/build
|
||||
mv c/org_ethereum_evmc_EvmcVm.h c/evmc-vm.h
|
||||
gcc $(DEBUG_FLAG) $(CFLAGS) -c $(INCLUDES) -o $(OUT_DIR)/loader.o ../../lib/loader/loader.c
|
||||
gcc $(DEBUG_FLAG) $(CFLAGS) -c $(INCLUDES) $(JAVA_INCLUDES) -o $(OUT_DIR)/host.o ./c/host.c
|
||||
gcc $(DEBUG_FLAG) $(CFLAGS) ./c/evmc-vm.c $(INCLUDES) $(JAVA_INCLUDES) $(JAVA_LIBS) $(CFLAGS) $(LFLAGS) -o $(OUT_DIR)/evmc.$(EXT) $(OUT_DIR)/host.o $(OUT_DIR)/loader.o
|
||||
gcc $(DEBUG_FLAG) -shared ../../examples/example_vm/example_vm.c $(INCLUDES) -o $(OUT_DIR)/example_vm.$(EXT)
|
||||
mkdir -p ./java/build
|
||||
./gradlew --no-daemon clean spotlessApply build
|
||||
|
||||
debug: DEBUG_FLAG = -D DEBUG
|
||||
|
||||
debug: build
|
||||
|
||||
test: build
|
||||
./gradlew --no-daemon test
|
||||
|
||||
format:
|
||||
clang-format -i c/evmc-vm.c c/host.c c/host.h
|
||||
|
||||
clean:
|
||||
rm -rf build
|
||||
rm -rf ./java/build/
|
||||
rm -rf ./c/build/
|
40
bindings/java/build.gradle
Normal file
40
bindings/java/build.gradle
Normal file
@ -0,0 +1,40 @@
|
||||
plugins {
|
||||
id 'com.diffplug.gradle.spotless' version '3.16.0'
|
||||
}
|
||||
apply from: "${rootDir}/wrapper.gradle"
|
||||
allprojects {
|
||||
apply plugin: 'java-library'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
sourceCompatibility = '11'
|
||||
targetCompatibility = '11'
|
||||
apply plugin: 'com.diffplug.gradle.spotless'
|
||||
spotless {
|
||||
java {
|
||||
// This path needs to be relative to each project
|
||||
target fileTree('.') {
|
||||
include '**/*.java'
|
||||
exclude '**/.gradle/**'
|
||||
}
|
||||
importOrder 'org.ethereum', 'java', ''
|
||||
trimTrailingWhitespace()
|
||||
endWithNewline()
|
||||
googleJavaFormat('1.7')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
tasks.withType(Test) {
|
||||
testLogging.showStandardStreams = true
|
||||
// If GRADLE_MAX_TEST_FORKS is not set, use half the available processors
|
||||
maxParallelForks = (System.getenv('GRADLE_MAX_TEST_FORKS') ?: (Runtime.runtime.availableProcessors().intdiv(2) ?: 1)).toInteger()
|
||||
useJUnitPlatform()
|
||||
reports {
|
||||
junitXml.enabled = true
|
||||
}
|
||||
}
|
||||
}
|
163
bindings/java/c/evmc-vm.c
Normal file
163
bindings/java/c/evmc-vm.c
Normal file
@ -0,0 +1,163 @@
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "evmc-vm.h"
|
||||
#include "evmc/helpers.h"
|
||||
#include "evmc/loader.h"
|
||||
#include "host.h"
|
||||
|
||||
JNIEXPORT jobject JNICALL Java_org_ethereum_evmc_EvmcVm_init(JNIEnv* jenv,
|
||||
jclass jcls,
|
||||
jstring jfilename)
|
||||
{
|
||||
struct evmc_vm* evm;
|
||||
jint rs = set_jvm(jenv);
|
||||
assert(rs == JNI_OK);
|
||||
// load the EVM
|
||||
const char* filename = (*jenv)->GetStringUTFChars(jenv, jfilename, 0);
|
||||
if (filename != NULL)
|
||||
{
|
||||
enum evmc_loader_error_code loader_error;
|
||||
evm = evmc_load_and_create(filename, &loader_error);
|
||||
if (evm == NULL || loader_error != EVMC_LOADER_SUCCESS)
|
||||
{
|
||||
const char* error_msg = evmc_last_error_msg();
|
||||
jclass jclazz = (*jenv)->FindClass(jenv, "java/lang/AssertionError");
|
||||
(*jenv)->ThrowNew(jenv, jclazz, error_msg);
|
||||
}
|
||||
(*jenv)->ReleaseStringUTFChars(jenv, jfilename, filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
jclass jclazz = (*jenv)->FindClass(jenv, "java/lang/AssertionError");
|
||||
(*jenv)->ThrowNew(jenv, jclazz, "JNI Error: filename cannot be NULL. \n");
|
||||
}
|
||||
jobject jresult = (*jenv)->NewDirectByteBuffer(jenv, (void*)evm, sizeof(struct evmc_vm));
|
||||
assert(jresult != NULL);
|
||||
return jresult;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_org_ethereum_evmc_EvmcVm_abi_1version(JNIEnv* jenv, jclass jcls)
|
||||
{
|
||||
return EVMC_ABI_VERSION;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_org_ethereum_evmc_EvmcVm_name(JNIEnv* jenv,
|
||||
jclass jcls,
|
||||
jobject jevm)
|
||||
{
|
||||
struct evmc_vm* evm = (struct evmc_vm*)(*jenv)->GetDirectBufferAddress(jenv, jevm);
|
||||
assert(evm != NULL);
|
||||
const char* evm_name = evmc_vm_name(evm);
|
||||
return (*jenv)->NewStringUTF(jenv, evm_name);
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_org_ethereum_evmc_EvmcVm_version(JNIEnv* jenv,
|
||||
jclass jcls,
|
||||
jobject jevm)
|
||||
{
|
||||
struct evmc_vm* evm = (struct evmc_vm*)(*jenv)->GetDirectBufferAddress(jenv, jevm);
|
||||
assert(evm != NULL);
|
||||
const char* evm_version = evmc_vm_version(evm);
|
||||
return (*jenv)->NewStringUTF(jenv, evm_version);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_ethereum_evmc_EvmcVm_destroy(JNIEnv* jenv,
|
||||
jclass jcls,
|
||||
jobject jevm)
|
||||
{
|
||||
struct evmc_vm* evm = (struct evmc_vm*)(*jenv)->GetDirectBufferAddress(jenv, jevm);
|
||||
assert(evm != NULL);
|
||||
evmc_destroy(evm);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_ethereum_evmc_EvmcVm_execute(JNIEnv* jenv,
|
||||
jclass jcls,
|
||||
jobject jevm,
|
||||
jint jcontext_index,
|
||||
jint jrev,
|
||||
jobject jmsg,
|
||||
jobject jcode,
|
||||
jint jsize,
|
||||
jobject jresult)
|
||||
{
|
||||
struct evmc_message* cmsg = (struct evmc_message*)(*jenv)->GetDirectBufferAddress(jenv, jmsg);
|
||||
assert(cmsg != NULL);
|
||||
const uint8_t* ccode = (uint8_t*)(*jenv)->GetDirectBufferAddress(jenv, jcode);
|
||||
assert(ccode != NULL);
|
||||
struct evmc_host_context context = {jcontext_index};
|
||||
struct evmc_vm* evm = (struct evmc_vm*)(*jenv)->GetDirectBufferAddress(jenv, jevm);
|
||||
assert(evm != NULL);
|
||||
#ifdef DEBUG
|
||||
printf("********************before execute*******************\n");
|
||||
|
||||
printf("struct: evmc_message=%p\n", cmsg);
|
||||
printf("sizeof(evmc_message): %lu\n", sizeof(struct evmc_message));
|
||||
printf("kind=%p\n", &cmsg->kind);
|
||||
printf("flags=%p\n", &cmsg->flags);
|
||||
printf("depth=%p\n", &cmsg->depth);
|
||||
printf("gas=%p\n", &cmsg->gas);
|
||||
printf("destination=%p\n", &cmsg->destination.bytes);
|
||||
printf("sender=%p\n", &cmsg->sender.bytes);
|
||||
printf("input_data=%p\n", &cmsg->input_data);
|
||||
printf("input_size=%p\n", &cmsg->input_size);
|
||||
printf("value=%p\n", &cmsg->value.bytes);
|
||||
printf("create2_salt=%p\n\n", &cmsg->create2_salt.bytes);
|
||||
|
||||
printf("kind=%d\n", cmsg->kind);
|
||||
printf("flags=%d\n", cmsg->flags);
|
||||
printf("depth=%d\n", cmsg->depth);
|
||||
printf("gas=%lld\n", cmsg->gas);
|
||||
printf("destination=%s\n", cmsg->destination.bytes);
|
||||
printf("sender=%s\n", cmsg->sender.bytes);
|
||||
printf("input_size=%zu\n", cmsg->input_size);
|
||||
printf("value=%s\n\n", cmsg->value.bytes);
|
||||
#endif
|
||||
const struct evmc_host_interface* host = get_host_interface();
|
||||
struct evmc_result* result =
|
||||
(struct evmc_result*)(*jenv)->GetDirectBufferAddress(jenv, jresult);
|
||||
assert(result != NULL);
|
||||
*result = evmc_execute(evm, host, &context, jrev, cmsg, ccode, jsize);
|
||||
#ifdef DEBUG
|
||||
printf("********************after execute*******************\n");
|
||||
printf("sizeof(evmc_result): %lu\n", sizeof(struct evmc_result));
|
||||
printf("status_code=%p\n", &result->status_code);
|
||||
printf("gas_left=%p\n", &result->gas_left);
|
||||
printf("output_data=%p\n\n", &result->output_data);
|
||||
|
||||
printf("status_code=%d\n", result->status_code);
|
||||
printf("gas_left=%llu\n", result->gas_left);
|
||||
printf("output_data=%s\n\n", result->output_data);
|
||||
#endif
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_org_ethereum_evmc_EvmcVm_get_1capabilities(JNIEnv* jenv,
|
||||
jclass jcls,
|
||||
jobject jevm)
|
||||
{
|
||||
struct evmc_vm* evm = (struct evmc_vm*)(*jenv)->GetDirectBufferAddress(jenv, jevm);
|
||||
assert(evm != NULL);
|
||||
return evm->get_capabilities(evm);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_org_ethereum_evmc_EvmcVm_set_1option(JNIEnv* jenv,
|
||||
jclass jcls,
|
||||
jobject jevm,
|
||||
jstring jname,
|
||||
jstring jvalue)
|
||||
{
|
||||
struct evmc_vm* evm = (struct evmc_vm*)(*jenv)->GetDirectBufferAddress(jenv, jevm);
|
||||
assert(evm != NULL);
|
||||
const char* name = (*jenv)->GetStringUTFChars(jenv, jname, 0);
|
||||
const char* value = (*jenv)->GetStringUTFChars(jenv, jvalue, 0);
|
||||
enum evmc_set_option_result option_result = evmc_set_option(evm, name, value);
|
||||
(*jenv)->ReleaseStringUTFChars(jenv, jname, name);
|
||||
(*jenv)->ReleaseStringUTFChars(jenv, jvalue, value);
|
||||
return option_result;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_org_ethereum_evmc_EvmcVm_get_1result_1size(JNIEnv* jenv, jclass jcls)
|
||||
{
|
||||
return sizeof(struct evmc_result);
|
||||
}
|
584
bindings/java/c/host.c
Normal file
584
bindings/java/c/host.c
Normal file
@ -0,0 +1,584 @@
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "host.h"
|
||||
|
||||
static JavaVM* jvm;
|
||||
|
||||
int set_jvm(JNIEnv* jenv)
|
||||
{
|
||||
return (*jenv)->GetJavaVM(jenv, &jvm);
|
||||
}
|
||||
|
||||
static JNIEnv* attach()
|
||||
{
|
||||
JNIEnv* jenv;
|
||||
jint rs = (*jvm)->AttachCurrentThread(jvm, (void**)&jenv, NULL);
|
||||
assert(rs == JNI_OK);
|
||||
return jenv;
|
||||
}
|
||||
|
||||
static bool account_exists_fn(struct evmc_host_context* context, const evmc_address* address)
|
||||
{
|
||||
bool result = false;
|
||||
const char java_method_name[] = "account_exists";
|
||||
const char java_method_signature[] = "(I[B)I";
|
||||
JNIEnv* jenv = attach();
|
||||
if (jenv != NULL)
|
||||
{
|
||||
jclass host_class;
|
||||
jmethodID method;
|
||||
jint jcontext_index;
|
||||
jbyteArray jaddress;
|
||||
|
||||
// get java class
|
||||
host_class = (*jenv)->FindClass(jenv, "org/ethereum/evmc/Host");
|
||||
assert(host_class != NULL);
|
||||
|
||||
// get java method
|
||||
method =
|
||||
(*jenv)->GetStaticMethodID(jenv, host_class, java_method_name, java_method_signature);
|
||||
assert(method != NULL);
|
||||
|
||||
// set java method params
|
||||
assert(context != NULL);
|
||||
jcontext_index = context->index;
|
||||
|
||||
jaddress = (*jenv)->NewByteArray(jenv, sizeof(struct evmc_address));
|
||||
assert(jaddress != NULL);
|
||||
(*jenv)->SetByteArrayRegion(jenv, jaddress, 0, sizeof(struct evmc_address),
|
||||
(jbyte*)address);
|
||||
|
||||
assert(jaddress != NULL);
|
||||
|
||||
// call java method
|
||||
jint jresult =
|
||||
(*jenv)->CallStaticIntMethod(jenv, host_class, method, jcontext_index, jaddress);
|
||||
result = !!jresult;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static evmc_bytes32 get_storage_fn(struct evmc_host_context* context,
|
||||
const evmc_address* address,
|
||||
const evmc_bytes32* key)
|
||||
{
|
||||
evmc_bytes32 result;
|
||||
const char java_method_name[] = "get_storage";
|
||||
const char java_method_signature[] = "(I[B[B)Ljava/nio/ByteBuffer;";
|
||||
JNIEnv* jenv = attach();
|
||||
if (jenv != NULL)
|
||||
{
|
||||
jclass host_class;
|
||||
jmethodID method;
|
||||
jint jcontext_index;
|
||||
jbyteArray jaddress;
|
||||
jbyteArray jkey;
|
||||
// get java class
|
||||
host_class = (*jenv)->FindClass(jenv, "org/ethereum/evmc/Host");
|
||||
assert(host_class != NULL);
|
||||
|
||||
// get java method
|
||||
method =
|
||||
(*jenv)->GetStaticMethodID(jenv, host_class, java_method_name, java_method_signature);
|
||||
assert(method != NULL);
|
||||
|
||||
// set java method params
|
||||
|
||||
assert(context != NULL);
|
||||
jcontext_index = context->index;
|
||||
|
||||
jaddress = (*jenv)->NewByteArray(jenv, sizeof(struct evmc_address));
|
||||
assert(jaddress != NULL);
|
||||
(*jenv)->SetByteArrayRegion(jenv, jaddress, 0, sizeof(struct evmc_address),
|
||||
(jbyte*)address);
|
||||
|
||||
jkey = (*jenv)->NewByteArray(jenv, sizeof(struct evmc_bytes32));
|
||||
assert(jkey != NULL);
|
||||
(*jenv)->SetByteArrayRegion(jenv, jkey, 0, sizeof(struct evmc_bytes32), (jbyte*)key);
|
||||
|
||||
// call java method
|
||||
jobject jresult = (*jenv)->CallStaticObjectMethod(jenv, host_class, method, jcontext_index,
|
||||
jaddress, jkey);
|
||||
|
||||
assert(jresult != NULL);
|
||||
evmc_bytes32* result_ptr =
|
||||
(struct evmc_bytes32*)(*jenv)->GetDirectBufferAddress(jenv, jresult);
|
||||
assert(result_ptr != NULL);
|
||||
result = *result_ptr;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static enum evmc_storage_status set_storage_fn(struct evmc_host_context* context,
|
||||
const evmc_address* address,
|
||||
const evmc_bytes32* key,
|
||||
const evmc_bytes32* value)
|
||||
{
|
||||
enum evmc_storage_status result;
|
||||
const char java_method_name[] = "set_storage";
|
||||
const char java_method_signature[] = "(I[B[B[B)I";
|
||||
JNIEnv* jenv = attach();
|
||||
if (jenv != NULL)
|
||||
{
|
||||
jclass host_class;
|
||||
jmethodID method;
|
||||
jint jcontext_index;
|
||||
jbyteArray jaddress;
|
||||
jbyteArray jkey;
|
||||
jbyteArray jvalue;
|
||||
// get java class
|
||||
host_class = (*jenv)->FindClass(jenv, "org/ethereum/evmc/Host");
|
||||
assert(host_class != NULL);
|
||||
// get java method
|
||||
method =
|
||||
(*jenv)->GetStaticMethodID(jenv, host_class, java_method_name, java_method_signature);
|
||||
assert(method != NULL);
|
||||
|
||||
// set java method params
|
||||
assert(context != NULL);
|
||||
jcontext_index = context->index;
|
||||
|
||||
jaddress = (*jenv)->NewByteArray(jenv, sizeof(struct evmc_address));
|
||||
assert(jaddress != NULL);
|
||||
(*jenv)->SetByteArrayRegion(jenv, jaddress, 0, sizeof(struct evmc_address),
|
||||
(jbyte*)address);
|
||||
|
||||
jkey = (*jenv)->NewByteArray(jenv, sizeof(struct evmc_bytes32));
|
||||
assert(jkey != NULL);
|
||||
(*jenv)->SetByteArrayRegion(jenv, jkey, 0, sizeof(struct evmc_bytes32), (jbyte*)key);
|
||||
|
||||
jvalue = (*jenv)->NewByteArray(jenv, sizeof(struct evmc_bytes32));
|
||||
assert(jvalue != NULL);
|
||||
(*jenv)->SetByteArrayRegion(jenv, jvalue, 0, sizeof(struct evmc_bytes32), (jbyte*)value);
|
||||
|
||||
// call java method
|
||||
jint jresult = (*jenv)->CallStaticIntMethod(jenv, host_class, method, jcontext_index,
|
||||
jaddress, jkey, jvalue);
|
||||
result = (enum evmc_storage_status)jresult;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static evmc_uint256be get_balance_fn(struct evmc_host_context* context, const evmc_address* address)
|
||||
{
|
||||
evmc_uint256be result;
|
||||
char java_method_name[] = "get_balance";
|
||||
char java_method_signature[] = "(I[B)Ljava/nio/ByteBuffer;";
|
||||
JNIEnv* jenv = attach();
|
||||
if (jenv != NULL)
|
||||
{
|
||||
jclass host_class;
|
||||
jmethodID method;
|
||||
jint jcontext_index;
|
||||
jbyteArray jaddress;
|
||||
|
||||
// get java class
|
||||
host_class = (*jenv)->FindClass(jenv, "org/ethereum/evmc/Host");
|
||||
assert(host_class != NULL);
|
||||
|
||||
// get java method
|
||||
method =
|
||||
(*jenv)->GetStaticMethodID(jenv, host_class, java_method_name, java_method_signature);
|
||||
assert(method != NULL);
|
||||
|
||||
// set java method params
|
||||
assert(context != NULL);
|
||||
jcontext_index = context->index;
|
||||
|
||||
jaddress = (*jenv)->NewByteArray(jenv, sizeof(struct evmc_address));
|
||||
assert(jaddress != NULL);
|
||||
(*jenv)->SetByteArrayRegion(jenv, jaddress, 0, sizeof(struct evmc_address),
|
||||
(jbyte*)address);
|
||||
|
||||
// call java method
|
||||
jobject jresult =
|
||||
(*jenv)->CallStaticObjectMethod(jenv, host_class, method, jcontext_index, jaddress);
|
||||
assert(jresult != NULL);
|
||||
|
||||
evmc_uint256be* result_ptr =
|
||||
(evmc_uint256be*)(*jenv)->GetDirectBufferAddress(jenv, jresult);
|
||||
assert(result_ptr != NULL);
|
||||
result = *result_ptr;
|
||||
|
||||
(*jenv)->ReleaseByteArrayElements(jenv, jaddress, (jbyte*)address, 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static size_t get_code_size_fn(struct evmc_host_context* context, const evmc_address* address)
|
||||
{
|
||||
size_t result = 0;
|
||||
char java_method_name[] = "get_code_size";
|
||||
char java_method_signature[] = "(I[B)I";
|
||||
JNIEnv* jenv = attach();
|
||||
if (jenv != NULL)
|
||||
{
|
||||
jclass host_class;
|
||||
jmethodID method;
|
||||
jint jcontext_index;
|
||||
jbyteArray jaddress;
|
||||
|
||||
// get java class
|
||||
host_class = (*jenv)->FindClass(jenv, "org/ethereum/evmc/Host");
|
||||
assert(host_class != NULL);
|
||||
|
||||
// get java method
|
||||
method =
|
||||
(*jenv)->GetStaticMethodID(jenv, host_class, java_method_name, java_method_signature);
|
||||
assert(method != NULL);
|
||||
|
||||
// set java method params
|
||||
assert(context != NULL);
|
||||
jcontext_index = context->index;
|
||||
|
||||
jaddress = (*jenv)->NewByteArray(jenv, sizeof(struct evmc_address));
|
||||
assert(jaddress != NULL);
|
||||
(*jenv)->SetByteArrayRegion(jenv, jaddress, 0, sizeof(struct evmc_address),
|
||||
(jbyte*)address);
|
||||
|
||||
// call java method
|
||||
jint jresult =
|
||||
(*jenv)->CallStaticIntMethod(jenv, host_class, method, jcontext_index, jaddress);
|
||||
result = jresult;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static evmc_bytes32 get_code_hash_fn(struct evmc_host_context* context, const evmc_address* address)
|
||||
{
|
||||
evmc_bytes32 result;
|
||||
char java_method_name[] = "get_code_hash";
|
||||
char java_method_signature[] = "(I[B)Ljava/nio/ByteBuffer;";
|
||||
JNIEnv* jenv = attach();
|
||||
if (jenv != NULL)
|
||||
{
|
||||
jclass host_class;
|
||||
jmethodID method;
|
||||
jint jcontext_index;
|
||||
jbyteArray jaddress;
|
||||
|
||||
// get java class
|
||||
host_class = (*jenv)->FindClass(jenv, "org/ethereum/evmc/Host");
|
||||
assert(host_class != NULL);
|
||||
// get java method
|
||||
method =
|
||||
(*jenv)->GetStaticMethodID(jenv, host_class, java_method_name, java_method_signature);
|
||||
assert(method != NULL);
|
||||
|
||||
// set java method params
|
||||
|
||||
assert(context != NULL);
|
||||
jcontext_index = context->index;
|
||||
|
||||
jaddress = (*jenv)->NewByteArray(jenv, sizeof(struct evmc_address));
|
||||
assert(jaddress != NULL);
|
||||
(*jenv)->SetByteArrayRegion(jenv, jaddress, 0, sizeof(struct evmc_address),
|
||||
(jbyte*)address);
|
||||
|
||||
// call java method
|
||||
jobject jresult =
|
||||
(*jenv)->CallStaticObjectMethod(jenv, host_class, method, jcontext_index, jaddress);
|
||||
assert(jresult != NULL);
|
||||
|
||||
evmc_bytes32* result_ptr =
|
||||
(struct evmc_bytes32*)(*jenv)->GetDirectBufferAddress(jenv, jresult);
|
||||
assert(result_ptr != NULL);
|
||||
result = *result_ptr;
|
||||
|
||||
(*jenv)->ReleaseByteArrayElements(jenv, jaddress, (jbyte*)address, 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static size_t copy_code_fn(struct evmc_host_context* context,
|
||||
const evmc_address* address,
|
||||
size_t code_offset,
|
||||
uint8_t* buffer_data,
|
||||
size_t buffer_size)
|
||||
{
|
||||
size_t result;
|
||||
const char java_method_name[] = "copy_code";
|
||||
const char java_method_signature[] = "(I[BI)Ljava/nio/ByteBuffer;";
|
||||
JNIEnv* jenv = attach();
|
||||
if (jenv != NULL)
|
||||
{
|
||||
jclass host_class;
|
||||
jmethodID method;
|
||||
jint jcontext_index;
|
||||
jbyteArray jaddress;
|
||||
jint jcode_offset;
|
||||
|
||||
// get java class
|
||||
host_class = (*jenv)->FindClass(jenv, "org/ethereum/evmc/Host");
|
||||
assert(host_class != NULL);
|
||||
|
||||
// get java method
|
||||
method =
|
||||
(*jenv)->GetStaticMethodID(jenv, host_class, java_method_name, java_method_signature);
|
||||
assert(method != NULL);
|
||||
|
||||
// set java method params
|
||||
|
||||
assert(context != NULL);
|
||||
jcontext_index = context->index;
|
||||
|
||||
jaddress = (*jenv)->NewByteArray(jenv, sizeof(struct evmc_address));
|
||||
assert(jaddress != NULL);
|
||||
(*jenv)->SetByteArrayRegion(jenv, jaddress, 0, sizeof(struct evmc_address),
|
||||
(jbyte*)address);
|
||||
|
||||
jcode_offset = code_offset;
|
||||
|
||||
// call java method
|
||||
jobject jresult = (*jenv)->CallStaticObjectMethod(jenv, host_class, method, jcontext_index,
|
||||
jaddress, jcode_offset);
|
||||
assert(jresult != NULL);
|
||||
|
||||
// copy jresult back to buffer_data
|
||||
buffer_data = (uint8_t*)(*jenv)->GetDirectBufferAddress(jenv, jresult);
|
||||
assert(buffer_data != NULL);
|
||||
|
||||
result = get_code_size_fn(context, address) - code_offset;
|
||||
|
||||
(*jenv)->ReleaseByteArrayElements(jenv, jaddress, (jbyte*)address, 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void selfdestruct_fn(struct evmc_host_context* context,
|
||||
const evmc_address* address,
|
||||
const evmc_address* beneficiary)
|
||||
{
|
||||
const char java_method_name[] = "selfdestruct";
|
||||
const char java_method_signature[] = "(I[B[B)V";
|
||||
JNIEnv* jenv = attach();
|
||||
if (jenv != NULL)
|
||||
{
|
||||
jclass host_class;
|
||||
jmethodID method;
|
||||
jint jcontext_index;
|
||||
jbyteArray jaddress;
|
||||
jbyteArray jbeneficiary;
|
||||
|
||||
// get java class
|
||||
host_class = (*jenv)->FindClass(jenv, "org/ethereum/evmc/Host");
|
||||
assert(host_class != NULL);
|
||||
|
||||
// get java method
|
||||
method =
|
||||
(*jenv)->GetStaticMethodID(jenv, host_class, java_method_name, java_method_signature);
|
||||
assert(method != NULL);
|
||||
|
||||
// set java method params
|
||||
assert(context != NULL);
|
||||
jcontext_index = context->index;
|
||||
|
||||
jaddress = (*jenv)->NewByteArray(jenv, sizeof(struct evmc_address));
|
||||
assert(jaddress != NULL);
|
||||
(*jenv)->SetByteArrayRegion(jenv, jaddress, 0, sizeof(struct evmc_address),
|
||||
(jbyte*)address);
|
||||
|
||||
jbeneficiary = (*jenv)->NewByteArray(jenv, sizeof(struct evmc_address));
|
||||
assert(jbeneficiary != NULL);
|
||||
(*jenv)->SetByteArrayRegion(jenv, jbeneficiary, 0, sizeof(struct evmc_address),
|
||||
(jbyte*)beneficiary);
|
||||
|
||||
// call java method
|
||||
(*jenv)->CallStaticIntMethod(jenv, host_class, method, jcontext_index, jaddress,
|
||||
jbeneficiary);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static struct evmc_result call_fn(struct evmc_host_context* context, const struct evmc_message* msg)
|
||||
{
|
||||
struct evmc_result result;
|
||||
const char java_method_name[] = "call";
|
||||
const char java_method_signature[] = "(ILjava/nio/ByteBuffer;)Ljava/nio/ByteBuffer;";
|
||||
JNIEnv* jenv = attach();
|
||||
if (jenv != NULL)
|
||||
{
|
||||
jclass host_class;
|
||||
jmethodID method;
|
||||
jint jcontext_index;
|
||||
jobject jmsg;
|
||||
|
||||
// get java class
|
||||
host_class = (*jenv)->FindClass(jenv, "org/ethereum/evmc/Host");
|
||||
assert(host_class != NULL);
|
||||
|
||||
// get java method
|
||||
method =
|
||||
(*jenv)->GetStaticMethodID(jenv, host_class, java_method_name, java_method_signature);
|
||||
assert(method != NULL);
|
||||
|
||||
// set java method params
|
||||
assert(context != NULL);
|
||||
jcontext_index = context->index;
|
||||
|
||||
jmsg = (*jenv)->NewDirectByteBuffer(jenv, (void*)msg, sizeof(struct evmc_message));
|
||||
assert(jmsg != NULL);
|
||||
|
||||
// call java method
|
||||
jobject jresult =
|
||||
(*jenv)->CallStaticObjectMethod(jenv, host_class, method, jcontext_index, jmsg);
|
||||
assert(jresult != NULL);
|
||||
|
||||
struct evmc_result* result_ptr =
|
||||
(struct evmc_result*)(*jenv)->GetDirectBufferAddress(jenv, jresult);
|
||||
assert(result_ptr != NULL);
|
||||
result = *result_ptr;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static struct evmc_tx_context get_tx_context_fn(struct evmc_host_context* context)
|
||||
{
|
||||
struct evmc_tx_context result;
|
||||
const char java_method_name[] = "get_tx_context";
|
||||
const char java_method_signature[] = "(I)Ljava/nio/ByteBuffer;";
|
||||
JNIEnv* jenv = attach();
|
||||
if (jenv != NULL)
|
||||
{
|
||||
jclass host_class;
|
||||
jmethodID method;
|
||||
jint jcontext_index;
|
||||
// get java class
|
||||
host_class = (*jenv)->FindClass(jenv, "org/ethereum/evmc/Host");
|
||||
assert(host_class != NULL);
|
||||
|
||||
// get java method
|
||||
method =
|
||||
(*jenv)->GetStaticMethodID(jenv, host_class, java_method_name, java_method_signature);
|
||||
assert(method != NULL);
|
||||
|
||||
// set java method params
|
||||
assert(context != NULL);
|
||||
jcontext_index = context->index;
|
||||
|
||||
// call java method
|
||||
jobject jresult = (*jenv)->CallStaticObjectMethod(jenv, host_class, method, jcontext_index);
|
||||
assert(jresult != NULL);
|
||||
|
||||
struct evmc_tx_context* result_ptr =
|
||||
(struct evmc_tx_context*)(*jenv)->GetDirectBufferAddress(jenv, jresult);
|
||||
assert(result_ptr != NULL);
|
||||
result = *result_ptr;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static evmc_bytes32 get_block_hash_fn(struct evmc_host_context* context, int64_t number)
|
||||
{
|
||||
evmc_bytes32 result;
|
||||
char java_method_name[] = "get_code_hash";
|
||||
char java_method_signature[] = "(IJ)Ljava/nio/ByteBuffer;";
|
||||
JNIEnv* jenv = attach();
|
||||
if (jenv != NULL)
|
||||
{
|
||||
jclass host_class;
|
||||
jmethodID method;
|
||||
jint jcontext_index;
|
||||
jlong jnumber;
|
||||
|
||||
// get java class
|
||||
host_class = (*jenv)->FindClass(jenv, "org/ethereum/evmc/Host");
|
||||
assert(host_class != NULL);
|
||||
|
||||
// get java method
|
||||
method =
|
||||
(*jenv)->GetStaticMethodID(jenv, host_class, java_method_name, java_method_signature);
|
||||
assert(method != NULL);
|
||||
|
||||
// set java method params
|
||||
|
||||
assert(context != NULL);
|
||||
jcontext_index = context->index;
|
||||
|
||||
jnumber = (jlong)number;
|
||||
|
||||
// call java method
|
||||
jobject jresult =
|
||||
(*jenv)->CallStaticObjectMethod(jenv, host_class, method, jcontext_index, jnumber);
|
||||
assert(jresult != NULL);
|
||||
|
||||
evmc_bytes32* result_ptr =
|
||||
(struct evmc_bytes32*)(*jenv)->GetDirectBufferAddress(jenv, jresult);
|
||||
assert(result_ptr != NULL);
|
||||
result = *result_ptr;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void emit_log_fn(struct evmc_host_context* context,
|
||||
const evmc_address* address,
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
const evmc_bytes32 topics[],
|
||||
size_t topics_count)
|
||||
{
|
||||
const char java_method_name[] = "emit_log";
|
||||
const char java_method_signature[] = "(I[B[BI[[BI)V";
|
||||
JNIEnv* jenv = attach();
|
||||
if (jenv != NULL)
|
||||
{
|
||||
jclass host_class;
|
||||
jmethodID method;
|
||||
jint jcontext_index;
|
||||
jbyteArray jaddress;
|
||||
jbyteArray jdata;
|
||||
jobjectArray jtopics;
|
||||
|
||||
// get java class
|
||||
host_class = (*jenv)->FindClass(jenv, "org/ethereum/evmc/Host");
|
||||
assert(host_class != NULL);
|
||||
|
||||
// get java method
|
||||
method =
|
||||
(*jenv)->GetStaticMethodID(jenv, host_class, java_method_name, java_method_signature);
|
||||
assert(method != NULL);
|
||||
|
||||
// set java method params
|
||||
assert(context != NULL);
|
||||
jcontext_index = context->index;
|
||||
|
||||
jaddress = (*jenv)->NewByteArray(jenv, sizeof(struct evmc_address));
|
||||
assert(jaddress != NULL);
|
||||
(*jenv)->SetByteArrayRegion(jenv, jaddress, 0, sizeof(struct evmc_address),
|
||||
(jbyte*)address);
|
||||
|
||||
jdata = (*jenv)->NewByteArray(jenv, data_size);
|
||||
assert(jdata != NULL);
|
||||
(*jenv)->SetByteArrayRegion(jenv, jdata, 0, data_size, (jbyte*)data);
|
||||
|
||||
jclass byte_type = (*jenv)->FindClass(jenv, "[B");
|
||||
jtopics = (*jenv)->NewObjectArray(jenv, (jsize)topics_count, byte_type, NULL);
|
||||
assert(jtopics != NULL);
|
||||
for (int i = 0; i < topics_count; i++)
|
||||
{
|
||||
jbyteArray jtopic = (*jenv)->NewByteArray(jenv, sizeof(struct evmc_bytes32));
|
||||
assert(jtopic != NULL);
|
||||
(*jenv)->SetByteArrayRegion(jenv, jtopic, 0, sizeof(struct evmc_bytes32),
|
||||
(jbyte*)topics[i].bytes);
|
||||
(*jenv)->SetObjectArrayElement(jenv, jtopics, i, jtopic);
|
||||
(*jenv)->DeleteLocalRef(jenv, jtopic);
|
||||
}
|
||||
|
||||
// call java method
|
||||
(*jenv)->CallStaticIntMethod(jenv, host_class, method, jcontext_index, jaddress, jdata,
|
||||
data_size, jtopics, topics_count);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const struct evmc_host_interface* get_host_interface()
|
||||
{
|
||||
static const struct evmc_host_interface host = {
|
||||
account_exists_fn, get_storage_fn, set_storage_fn, get_balance_fn,
|
||||
get_code_size_fn, get_code_hash_fn, copy_code_fn, selfdestruct_fn,
|
||||
call_fn, get_tx_context_fn, get_block_hash_fn, emit_log_fn,
|
||||
};
|
||||
return &host;
|
||||
}
|
21
bindings/java/c/host.h
Normal file
21
bindings/java/c/host.h
Normal file
@ -0,0 +1,21 @@
|
||||
#include "evmc/evmc.h"
|
||||
#include <jni.h>
|
||||
|
||||
#ifndef _Included_org_ethereum_evmc_Host
|
||||
#define _Included_org_ethereum_evmc_Host
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct evmc_host_context
|
||||
{
|
||||
int index;
|
||||
};
|
||||
|
||||
int set_jvm(JNIEnv*);
|
||||
const struct evmc_host_interface* get_host_interface();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
26
bindings/java/java/README.md
Normal file
26
bindings/java/java/README.md
Normal file
@ -0,0 +1,26 @@
|
||||
## Java Bindings
|
||||
|
||||
This bindings have been tested with `openjdk64-11.0.1` on OSX and debian:latest.
|
||||
For examples of how to use these bindings take a look at the JUnit tests.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
You need to have [Gradle](https://www.gradle.org/installation) and [Java](https://www.oracle.com/technetwork/java/javase/downloads/index.html) installed.
|
||||
|
||||
> Note: Requires Gradle 5.x
|
||||
|
||||
### Build and test
|
||||
|
||||
from project root:
|
||||
|
||||
```bash
|
||||
cd bindings/java && make clean build test
|
||||
```
|
||||
|
||||
### Build and test (debug with debug printouts)
|
||||
|
||||
from project root:
|
||||
|
||||
```bash
|
||||
cd bindings/java && make clean debug test
|
||||
```
|
7
bindings/java/java/build.gradle
Normal file
7
bindings/java/java/build.gradle
Normal file
@ -0,0 +1,7 @@
|
||||
dependencies {
|
||||
testImplementation 'org.apache.commons:commons-lang3:3.0'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.2'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.3.2'
|
||||
|
||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.2'
|
||||
}
|
166
bindings/java/java/src/main/java/org/ethereum/evmc/EvmcVm.java
Normal file
166
bindings/java/java/src/main/java/org/ethereum/evmc/EvmcVm.java
Normal file
@ -0,0 +1,166 @@
|
||||
package org.ethereum.evmc;
|
||||
|
||||
import static org.ethereum.evmc.Host.addContext;
|
||||
import static org.ethereum.evmc.Host.removeContext;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* The Java interface to the evm instance.
|
||||
*
|
||||
* <p>Defines the Java methods capable of accessing the evm implementation.
|
||||
*/
|
||||
public final class EvmcVm implements AutoCloseable {
|
||||
private static EvmcVm evmcVm;
|
||||
private static boolean isEvmcLibraryLoaded = false;
|
||||
private ByteBuffer nativeVm;
|
||||
/**
|
||||
* This method loads the specified evm shared library and loads/initializes the jni bindings.
|
||||
*
|
||||
* @param filename /path/filename of the evm shared object
|
||||
*/
|
||||
public static EvmcVm create(String filename) {
|
||||
if (!EvmcVm.isEvmcLibraryLoaded) {
|
||||
try {
|
||||
// load so containing the jni bindings to evmc
|
||||
System.load(System.getProperty("user.dir") + "/../c/build/evmc.so");
|
||||
EvmcVm.isEvmcLibraryLoaded = true;
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
System.err.println("Native code library failed to load.\n" + e);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
if (Objects.isNull(evmcVm)) {
|
||||
evmcVm = new EvmcVm(filename);
|
||||
}
|
||||
return evmcVm;
|
||||
}
|
||||
|
||||
private EvmcVm(String filename) {
|
||||
// initialize jni and load EVM shared library
|
||||
nativeVm = init(filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method loads the specified evm implementation and initializes jni
|
||||
*
|
||||
* @param filename path + filename of the evm shared object to load
|
||||
* @return
|
||||
*/
|
||||
public native ByteBuffer init(String filename);
|
||||
|
||||
/**
|
||||
* EVMC ABI version implemented by the VM instance.
|
||||
*
|
||||
* <p>Can be used to detect ABI incompatibilities. The EVMC ABI version represented by this file
|
||||
* is in ::EVMC_ABI_VERSION.
|
||||
*/
|
||||
public native int abi_version();
|
||||
|
||||
/**
|
||||
* The name of the EVMC VM implementation.
|
||||
*
|
||||
* <p>It MUST be a NULL-terminated not empty string. The content MUST be UTF-8 encoded (this
|
||||
* implies ASCII encoding is also allowed).
|
||||
*/
|
||||
native String name(ByteBuffer nativeVm);
|
||||
|
||||
/** Function is a wrapper around native name(). */
|
||||
public String name() {
|
||||
return name(nativeVm);
|
||||
}
|
||||
|
||||
/**
|
||||
* The version of the EVMC VM implementation, e.g. "1.2.3b4".
|
||||
*
|
||||
* <p>It MUST be a NULL-terminated not empty string. The content MUST be UTF-8 encoded (this
|
||||
* implies ASCII encoding is also allowed).
|
||||
*/
|
||||
native String version(ByteBuffer nativeVm);
|
||||
|
||||
/** Function is a wrapper around native version(). */
|
||||
public String version() {
|
||||
return version(nativeVm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to destroy the VM instance.
|
||||
*
|
||||
* <p>This is a mandatory method and MUST NOT be set to NULL.
|
||||
*/
|
||||
native void destroy(ByteBuffer nativeVm);
|
||||
|
||||
/**
|
||||
* Function to execute a code by the VM instance.
|
||||
*
|
||||
* <p>This is a mandatory method and MUST NOT be set to NULL.
|
||||
*/
|
||||
native void execute(
|
||||
ByteBuffer nativeVm,
|
||||
int context_index,
|
||||
int rev,
|
||||
ByteBuffer msg,
|
||||
ByteBuffer code,
|
||||
int size,
|
||||
ByteBuffer result);
|
||||
|
||||
/**
|
||||
* Function is a wrapper around native execute.
|
||||
*
|
||||
* <p>This allows the context to managed in one method
|
||||
*/
|
||||
public synchronized ByteBuffer execute(
|
||||
HostContext context, int rev, ByteBuffer msg, ByteBuffer code, int size) {
|
||||
int context_index = addContext(context);
|
||||
int resultSize = get_result_size();
|
||||
ByteBuffer result = ByteBuffer.allocateDirect(resultSize);
|
||||
execute(nativeVm, context_index, rev, msg, code, size, result);
|
||||
removeContext(context_index);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* A method returning capabilities supported by the VM instance.
|
||||
*
|
||||
* <p>The value returned MAY change when different options are set via the set_option() method.
|
||||
*
|
||||
* <p>A Client SHOULD only rely on the value returned if it has queried it after it has called the
|
||||
* set_option().
|
||||
*
|
||||
* <p>This is a mandatory method and MUST NOT be set to NULL.
|
||||
*/
|
||||
native int get_capabilities(ByteBuffer nativeVm);
|
||||
|
||||
/** Function is a wrapper around native get_capabilities(). */
|
||||
public int get_capabilities() {
|
||||
return get_capabilities(nativeVm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that modifies VM's options.
|
||||
*
|
||||
* <p>If the VM does not support this feature the pointer can be NULL.
|
||||
*/
|
||||
native int set_option(ByteBuffer nativeVm, String name, String value);
|
||||
|
||||
/** Function is a wrapper around native set_option(). */
|
||||
public int set_option(String name, String value) {
|
||||
return set_option(nativeVm, name, value);
|
||||
}
|
||||
|
||||
/** get size of result struct */
|
||||
native int get_result_size();
|
||||
|
||||
/**
|
||||
* This method cleans up resources
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
destroy(nativeVm);
|
||||
isEvmcLibraryLoaded = false;
|
||||
evmcVm = null;
|
||||
}
|
||||
}
|
150
bindings/java/java/src/main/java/org/ethereum/evmc/Host.java
Normal file
150
bindings/java/java/src/main/java/org/ethereum/evmc/Host.java
Normal file
@ -0,0 +1,150 @@
|
||||
package org.ethereum.evmc;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The Host interface.
|
||||
*
|
||||
* <p>The set of all callback functions expected by VM instances.
|
||||
*/
|
||||
final class Host {
|
||||
/** Check account existence callback function. */
|
||||
static int account_exists(int context_index, byte[] address) {
|
||||
HostContext context =
|
||||
requireNonNull(
|
||||
getContext(context_index),
|
||||
"HostContext does not exist for context_index: " + context_index);
|
||||
return context.accountExists(address) ? 1 : 0;
|
||||
}
|
||||
|
||||
/** Get storage callback function. */
|
||||
static ByteBuffer get_storage(int context_index, byte[] address, byte[] key) {
|
||||
HostContext context =
|
||||
requireNonNull(
|
||||
getContext(context_index),
|
||||
"HostContext does not exist for context_index: " + context_index);
|
||||
return context.getStorage(address, key);
|
||||
}
|
||||
|
||||
/** Set storage callback function. */
|
||||
static int set_storage(int context_index, byte[] address, byte[] key, byte[] value) {
|
||||
HostContext context =
|
||||
requireNonNull(
|
||||
getContext(context_index),
|
||||
"HostContext does not exist for context_index: " + context_index);
|
||||
return context.setStorage(address, key, value);
|
||||
}
|
||||
/** Get balance callback function. */
|
||||
static ByteBuffer get_balance(int context_index, byte[] address) {
|
||||
HostContext context =
|
||||
requireNonNull(
|
||||
getContext(context_index),
|
||||
"HostContext does not exist for context_index: " + context_index);
|
||||
return context.getBalance(address);
|
||||
}
|
||||
|
||||
/** Get code size callback function. */
|
||||
static int get_code_size(int context_index, byte[] address) {
|
||||
HostContext context =
|
||||
requireNonNull(
|
||||
getContext(context_index),
|
||||
"HostContext does not exist for context_index: " + context_index);
|
||||
return context.getCodeSize(address);
|
||||
}
|
||||
|
||||
/** Get code hash callback function. */
|
||||
static ByteBuffer get_code_hash(int context_index, byte[] address) {
|
||||
HostContext context =
|
||||
requireNonNull(
|
||||
getContext(context_index),
|
||||
"HostContext does not exist for context_index: " + context_index);
|
||||
return context.getCodeHash(address);
|
||||
}
|
||||
|
||||
/** Copy code callback function. */
|
||||
static ByteBuffer copy_code(int context_index, byte[] address, int code_offset) {
|
||||
HostContext context =
|
||||
requireNonNull(
|
||||
getContext(context_index),
|
||||
"HostContext does not exist for context_index: " + context_index);
|
||||
byte[] code = context.getCode(address).array();
|
||||
|
||||
if (code != null && code_offset > 0 && code_offset < code.length) {
|
||||
int length = code.length - code_offset;
|
||||
return ByteBuffer.wrap(code, 0, length);
|
||||
}
|
||||
|
||||
return ByteBuffer.wrap(new byte[0]);
|
||||
}
|
||||
|
||||
/** Selfdestruct callback function. */
|
||||
static void selfdestruct(int context_index, byte[] address, byte[] beneficiary) {
|
||||
HostContext context =
|
||||
requireNonNull(
|
||||
getContext(context_index),
|
||||
"HostContext does not exist for context_index: " + context_index);
|
||||
context.selfdestruct(address, beneficiary);
|
||||
}
|
||||
|
||||
/** Call callback function. */
|
||||
static ByteBuffer call(int context_index, ByteBuffer msg) {
|
||||
HostContext context =
|
||||
requireNonNull(
|
||||
getContext(context_index),
|
||||
"HostContext does not exist for context_index: " + context_index);
|
||||
return context.call(msg);
|
||||
}
|
||||
|
||||
/** Get transaction context callback function. */
|
||||
static ByteBuffer get_tx_context(int context_index) {
|
||||
HostContext context =
|
||||
requireNonNull(
|
||||
getContext(context_index),
|
||||
"HostContext does not exist for context_index: " + context_index);
|
||||
return context.getTxContext();
|
||||
}
|
||||
|
||||
/** Get block hash callback function. */
|
||||
static ByteBuffer get_block_hash_fn(int context_index, long number) {
|
||||
HostContext context =
|
||||
requireNonNull(
|
||||
getContext(context_index),
|
||||
"HostContext does not exist for context_index: " + context_index);
|
||||
return context.getBlockHash(number);
|
||||
}
|
||||
|
||||
/** Emit log callback function. */
|
||||
static void emit_log(
|
||||
int context_index,
|
||||
byte[] address,
|
||||
byte[] data,
|
||||
int data_size,
|
||||
byte[][] topics,
|
||||
int topic_count) {
|
||||
HostContext context =
|
||||
requireNonNull(
|
||||
getContext(context_index),
|
||||
"HostContext does not exist for context_index: " + context_index);
|
||||
context.emitLog(address, data, data_size, topics, topic_count);
|
||||
}
|
||||
|
||||
static HostContext getContext(int index) {
|
||||
return contextList.get(index);
|
||||
}
|
||||
|
||||
static synchronized int addContext(HostContext context) {
|
||||
contextList.add(context);
|
||||
return contextList.size() - 1;
|
||||
}
|
||||
|
||||
static void removeContext(int index) {
|
||||
contextList.remove(index);
|
||||
}
|
||||
|
||||
private static List<HostContext> contextList = Collections.synchronizedList(new ArrayList<>());
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
package org.ethereum.evmc;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* This interface represents the callback functions must be implemented in order to interface with
|
||||
* the EVM.
|
||||
*/
|
||||
public interface HostContext {
|
||||
/**
|
||||
* Check account existence function.
|
||||
*
|
||||
* <p>This function is used by the VM to check if there exists an account at given address.
|
||||
*
|
||||
* @param address The address of the account the query is about.
|
||||
* @return true if exists, false otherwise.
|
||||
*/
|
||||
boolean accountExists(byte[] address);
|
||||
|
||||
/**
|
||||
* Get storage function.
|
||||
*
|
||||
* <p>This function is used by a VM to query the given account storage entry.
|
||||
*
|
||||
* @param address The address of the account.
|
||||
* @param key The index of the account's storage entry.
|
||||
* @return The storage value at the given storage key or null bytes if the account does not exist.
|
||||
*/
|
||||
ByteBuffer getStorage(byte[] address, byte[] key);
|
||||
|
||||
/**
|
||||
* Set storage function.
|
||||
*
|
||||
* <p>This function is used by a VM to update the given account storage entry. The VM MUST make
|
||||
* sure that the account exists. This requirement is only a formality because VM implementations
|
||||
* only modify storage of the account of the current execution context (i.e. referenced by
|
||||
* evmc_message::destination).
|
||||
*
|
||||
* @param address The address of the account.
|
||||
* @param key The index of the storage entry.
|
||||
* @param value The value to be stored.
|
||||
* @return The effect on the storage item.
|
||||
*/
|
||||
int setStorage(byte[] address, byte[] key, byte[] value);
|
||||
|
||||
/**
|
||||
* Get balance function.
|
||||
*
|
||||
* <p>This function is used by a VM to query the balance of the given account.
|
||||
*
|
||||
* @param address The address of the account.
|
||||
* @return The balance of the given account or 0 if the account does not exist.
|
||||
*/
|
||||
ByteBuffer getBalance(byte[] address);
|
||||
|
||||
/**
|
||||
* Get code size function.
|
||||
*
|
||||
* <p>This function is used by a VM to get the size of the code stored in the account at the given
|
||||
* address.
|
||||
*
|
||||
* @param address The address of the account.
|
||||
* @return The size of the code in the account or 0 if the account does not exist.
|
||||
*/
|
||||
int getCodeSize(byte[] address);
|
||||
|
||||
/**
|
||||
* Get code hash function.
|
||||
*
|
||||
* <p>This function is used by a VM to get the keccak256 hash of the code stored in the account at
|
||||
* the given address. For existing accounts not having a code, this function returns keccak256
|
||||
* hash of empty data.
|
||||
*
|
||||
* @param address The address of the account.
|
||||
* @return The hash of the code in the account or null bytes if the account does not exist.
|
||||
*/
|
||||
ByteBuffer getCodeHash(byte[] address);
|
||||
|
||||
/**
|
||||
* Copy code function.
|
||||
*
|
||||
* <p>This function is used by an EVM to request a copy of the code of the given account to the
|
||||
* memory buffer provided by the EVM. The Client MUST copy the requested code, starting with the
|
||||
* given offset, to the provided memory buffer up to the size of the buffer or the size of the
|
||||
* code, whichever is smaller.
|
||||
*
|
||||
* @param address The address of the account.
|
||||
* @return A copy of the requested code.
|
||||
*/
|
||||
ByteBuffer getCode(byte[] address);
|
||||
|
||||
/**
|
||||
* Selfdestruct function.
|
||||
*
|
||||
* <p>This function is used by an EVM to SELFDESTRUCT given contract. The execution of the
|
||||
* contract will not be stopped, that is up to the EVM.
|
||||
*
|
||||
* @param address The address of the contract to be selfdestructed.
|
||||
* @param beneficiary The address where the remaining ETH is going to be transferred.
|
||||
*/
|
||||
void selfdestruct(byte[] address, byte[] beneficiary);
|
||||
|
||||
/**
|
||||
* This function supports EVM calls.
|
||||
*
|
||||
* @param msg The call parameters.
|
||||
* @return The result of the call.
|
||||
*/
|
||||
ByteBuffer call(ByteBuffer msg);
|
||||
|
||||
/**
|
||||
* Get transaction context function.
|
||||
*
|
||||
* <p>This function is used by an EVM to retrieve the transaction and block context.
|
||||
*
|
||||
* @return The transaction context.
|
||||
*/
|
||||
ByteBuffer getTxContext();
|
||||
|
||||
/**
|
||||
* Get block hash function.
|
||||
*
|
||||
* <p>This function is used by a VM to query the hash of the header of the given block. If the
|
||||
* information about the requested block is not available, then this is signalled by returning
|
||||
* null bytes.
|
||||
*
|
||||
* @param number The block number.
|
||||
* @return The block hash or null bytes if the information about the block is not available.
|
||||
*/
|
||||
ByteBuffer getBlockHash(long number);
|
||||
|
||||
/**
|
||||
* Log function.
|
||||
*
|
||||
* <p>This function is used by an EVM to inform about a LOG that happened during an EVM bytecode
|
||||
* execution.
|
||||
*
|
||||
* @param address The address of the contract that generated the log.
|
||||
* @param data The unindexed data attached to the log.
|
||||
* @param dataSize The length of the data.
|
||||
* @param topics The the array of topics attached to the log.
|
||||
* @param topicCount The number of the topics. Valid values are between 0 and 4 inclusively.
|
||||
*/
|
||||
void emitLog(byte[] address, byte[] data, int dataSize, byte[][] topics, int topicCount);
|
||||
}
|
247
bindings/java/java/src/test/java/org/ethereum/evmc/EvmcTest.java
Normal file
247
bindings/java/java/src/test/java/org/ethereum/evmc/EvmcTest.java
Normal file
@ -0,0 +1,247 @@
|
||||
package org.ethereum.evmc;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class EvmcTest {
|
||||
@Test
|
||||
void testInitCloseDestroy() throws Exception {
|
||||
Assertions.assertDoesNotThrow(
|
||||
() -> {
|
||||
try (EvmcVm vm =
|
||||
EvmcVm.create(System.getProperty("user.dir") + "/../c/build/example_vm.so")) {}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAbiVersion() throws Exception {
|
||||
try (EvmcVm vm = EvmcVm.create(System.getProperty("user.dir") + "/../c/build/example_vm.so")) {
|
||||
int abiVersion = vm.abi_version();
|
||||
assert (abiVersion > 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testName() throws Exception {
|
||||
try (EvmcVm vm = EvmcVm.create(System.getProperty("user.dir") + "/../c/build/example_vm.so")) {
|
||||
String name = vm.name();
|
||||
assert (name.length() > 0);
|
||||
assert (name.equals("example_vm"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVersion() throws Exception {
|
||||
try (EvmcVm vm = EvmcVm.create(System.getProperty("user.dir") + "/../c/build/example_vm.so")) {
|
||||
String version = vm.version();
|
||||
assert (version.length() > 0);
|
||||
assert (version.equals("0.0.0"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExecute_returnAddress() throws Exception {
|
||||
try (EvmcVm vm = EvmcVm.create(System.getProperty("user.dir") + "/../c/build/example_vm.so")) {
|
||||
HostContext context = new TestHostContext();
|
||||
int BYZANTIUM = 4;
|
||||
int EVMC_CALL = 0;
|
||||
int kind = EVMC_CALL;
|
||||
char[] sender = "39bf71de1b7d7be3b51\0".toCharArray();
|
||||
char[] destination = "53cf77204eEef952e25\0".toCharArray();
|
||||
char[] value = "1\0".toCharArray();
|
||||
char[] inputData = "hello w\0".toCharArray();
|
||||
long gas = 200000;
|
||||
int depth = 0;
|
||||
ByteBuffer msg =
|
||||
new TestMessage(kind, sender, destination, value, inputData, gas, depth).toByteBuffer();
|
||||
|
||||
byte[] code = {0x30, 0x60, 0x00, 0x52, 0x59, 0x60, 0x00, (byte) 0xf3}; // return_address
|
||||
ByteBuffer bbcode = ByteBuffer.allocateDirect(code.length).put(code);
|
||||
|
||||
ByteBuffer result =
|
||||
vm.execute(context, BYZANTIUM, msg, bbcode, code.length).order(ByteOrder.nativeOrder());
|
||||
int statusCode = result.getInt();
|
||||
result.getInt(); // padding
|
||||
long gasLeft = result.getLong();
|
||||
assert (statusCode == 0);
|
||||
assert (gasLeft == 0);
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests callbacks: get_storage_fn & set_storage_fn */
|
||||
@Test
|
||||
void testExecute_counter() throws Exception {
|
||||
try (EvmcVm vm = EvmcVm.create(System.getProperty("user.dir") + "/../c/build/example_vm.so")) {
|
||||
HostContext context = new TestHostContext();
|
||||
int BYZANTIUM = 4;
|
||||
int EVMC_CALL = 0;
|
||||
int kind = EVMC_CALL;
|
||||
char[] sender = "39bf71de1b7d7be3b51\0".toCharArray();
|
||||
char[] destination = "53cf77204eEef952e25\0".toCharArray();
|
||||
char[] value = "1\0".toCharArray();
|
||||
char[] inputData = "hello w\0".toCharArray();
|
||||
long gas = 200000;
|
||||
int depth = 0;
|
||||
ByteBuffer msg =
|
||||
new TestMessage(kind, sender, destination, value, inputData, gas, depth).toByteBuffer();
|
||||
|
||||
byte[] code = {0x60, 0x01, 0x60, 0x00, 0x54, 0x01, 0x60, 0x00, 0x55}; // counter
|
||||
ByteBuffer bbcode = ByteBuffer.allocateDirect(code.length).put(code);
|
||||
|
||||
ByteBuffer result =
|
||||
vm.execute(context, BYZANTIUM, msg, bbcode, code.length).order(ByteOrder.nativeOrder());
|
||||
int statusCode = result.getInt();
|
||||
result.getInt(); // padding
|
||||
long gasLeft = result.getLong();
|
||||
assert (statusCode == 0);
|
||||
assert (gasLeft == 0);
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests callbacks: get_tx_context_fn */
|
||||
@Test
|
||||
void testExecute_returnBlockNumber() throws Exception {
|
||||
try (EvmcVm vm = EvmcVm.create(System.getProperty("user.dir") + "/../c/build/example_vm.so")) {
|
||||
HostContext context = new TestHostContext();
|
||||
int BYZANTIUM = 4;
|
||||
int EVMC_CALL = 0;
|
||||
int kind = EVMC_CALL;
|
||||
char[] sender = "39bf71de1b7d7be3b51\0".toCharArray();
|
||||
char[] destination = "53cf77204eEef952e25\0".toCharArray();
|
||||
char[] value = "1\0".toCharArray();
|
||||
char[] inputData = "hello w\0".toCharArray();
|
||||
long gas = 200000;
|
||||
int depth = 0;
|
||||
ByteBuffer msg =
|
||||
new TestMessage(kind, sender, destination, value, inputData, gas, depth).toByteBuffer();
|
||||
|
||||
byte[] code = {0x43, 0x60, 0x00, 0x52, 0x59, 0x60, 0x00, (byte) 0xf3}; // return_block_number(
|
||||
ByteBuffer bbcode = ByteBuffer.allocateDirect(code.length).put(code);
|
||||
|
||||
ByteBuffer result =
|
||||
vm.execute(context, BYZANTIUM, msg, bbcode, code.length).order(ByteOrder.nativeOrder());
|
||||
int statusCode = result.getInt();
|
||||
result.getInt(); // padding
|
||||
long gasLeft = result.getLong();
|
||||
assert (statusCode == 0);
|
||||
assert (gasLeft == gas / 2);
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests callbacks: get_tx_context_fn & set_storage_fn */
|
||||
@Test
|
||||
void testExecute_saveReturnBlockNumber() throws Exception {
|
||||
try (EvmcVm vm = EvmcVm.create(System.getProperty("user.dir") + "/../c/build/example_vm.so")) {
|
||||
HostContext context = new TestHostContext();
|
||||
int BYZANTIUM = 4;
|
||||
int EVMC_CALL = 0;
|
||||
int kind = EVMC_CALL;
|
||||
char[] sender = "39bf71de1b7d7be3b51\0".toCharArray();
|
||||
char[] destination = "53cf77204eEef952e25\0".toCharArray();
|
||||
char[] value = "1\0".toCharArray();
|
||||
char[] inputData = "hello w\0".toCharArray();
|
||||
long gas = 200000;
|
||||
int depth = 0;
|
||||
ByteBuffer msg =
|
||||
new TestMessage(kind, sender, destination, value, inputData, gas, depth).toByteBuffer();
|
||||
|
||||
byte[] code = {
|
||||
0x43, 0x60, 0x00, 0x55, 0x43, 0x60, 0x00, 0x52, 0x59, 0x60, 0x00, (byte) 0xf3
|
||||
}; // save_return_block_number
|
||||
ByteBuffer bbcode = ByteBuffer.allocateDirect(code.length).put(code);
|
||||
|
||||
ByteBuffer result =
|
||||
vm.execute(context, BYZANTIUM, msg, bbcode, code.length).order(ByteOrder.nativeOrder());
|
||||
int statusCode = result.getInt();
|
||||
result.getInt(); // padding
|
||||
long gasLeft = result.getLong();
|
||||
assert (statusCode == 0);
|
||||
assert (gasLeft == gas / 2);
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests callbacks: call_fn */
|
||||
@Test
|
||||
void testExecute_makeCall() throws Exception {
|
||||
try (EvmcVm vm = EvmcVm.create(System.getProperty("user.dir") + "/../c/build/example_vm.so")) {
|
||||
HostContext context = new TestHostContext();
|
||||
int BYZANTIUM = 4;
|
||||
int EVMC_CALL = 0;
|
||||
int kind = EVMC_CALL;
|
||||
char[] sender = "39bf71de1b7d7be3b51\0".toCharArray();
|
||||
char[] destination = "53cf77204eEef952e25\0".toCharArray();
|
||||
char[] value = "1\0".toCharArray();
|
||||
char[] inputData = "hello w\0".toCharArray();
|
||||
long gas = 200000;
|
||||
int depth = 0;
|
||||
ByteBuffer msg =
|
||||
new TestMessage(kind, sender, destination, value, inputData, gas, depth).toByteBuffer();
|
||||
byte[] code = {
|
||||
0x60,
|
||||
0x00,
|
||||
(byte) 0x80,
|
||||
(byte) 0x80,
|
||||
(byte) 0x80,
|
||||
(byte) 0x80,
|
||||
(byte) 0x80,
|
||||
(byte) 0x80,
|
||||
(byte) 0xf1
|
||||
}; // make_a_call(
|
||||
ByteBuffer bbcode = ByteBuffer.allocateDirect(code.length).put(code);
|
||||
|
||||
ByteBuffer result =
|
||||
vm.execute(context, BYZANTIUM, msg, bbcode, code.length).order(ByteOrder.nativeOrder());
|
||||
int statusCode = result.getInt();
|
||||
result.getInt(); // padding
|
||||
long gasLeft = result.getLong();
|
||||
assert (statusCode == 0);
|
||||
assert (gasLeft == 0); // gas - gas / 64);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExecute_EVMC_CREATE() throws Exception {
|
||||
try (EvmcVm vm = EvmcVm.create(System.getProperty("user.dir") + "/../c/build/example_vm.so")) {
|
||||
HostContext context = new TestHostContext();
|
||||
int BYZANTIUM = 4;
|
||||
int EVMC_CREATE = 3;
|
||||
int kind = EVMC_CREATE;
|
||||
char[] sender = "39bf71de1b7d7be3b51\\0".toCharArray();
|
||||
char[] destination = "53cf77204eEef952e25\0".toCharArray();
|
||||
char[] value = "1\0".toCharArray();
|
||||
char[] inputData = "hello w\0".toCharArray();
|
||||
long gas = 200000;
|
||||
int depth = 0;
|
||||
ByteBuffer msg =
|
||||
new TestMessage(kind, sender, destination, value, inputData, gas, depth).toByteBuffer();
|
||||
byte[] code = {0x00};
|
||||
ByteBuffer bbcode = ByteBuffer.allocateDirect(code.length).put(code);
|
||||
|
||||
ByteBuffer result =
|
||||
vm.execute(context, BYZANTIUM, msg, bbcode, code.length).order(ByteOrder.nativeOrder());
|
||||
int statusCode = result.getInt();
|
||||
result.getInt(); // padding
|
||||
long gasLeft = result.getLong();
|
||||
assert (statusCode == 0);
|
||||
assert (gasLeft == gas / 10);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetCapabilities() throws Exception {
|
||||
try (EvmcVm vm = EvmcVm.create(System.getProperty("user.dir") + "/../c/build/example_vm.so")) {
|
||||
int capabilities = vm.get_capabilities();
|
||||
assert (capabilities > 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetOption() throws Exception {
|
||||
try (EvmcVm vm = EvmcVm.create(System.getProperty("user.dir") + "/../c/build/example_vm.so")) {
|
||||
int result = vm.set_option("verbose", "1");
|
||||
assert (result == 0);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package org.ethereum.evmc;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
class TestHostContext implements HostContext {
|
||||
@Override
|
||||
public boolean accountExists(byte[] address) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getStorage(byte[] address, byte[] key) {
|
||||
return ByteBuffer.allocateDirect(64).put(new byte[64]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setStorage(byte[] address, byte[] key, byte[] value) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getBalance(byte[] address) {
|
||||
return ByteBuffer.allocateDirect(64).put(new byte[64]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCodeSize(byte[] address) {
|
||||
return address.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getCodeHash(byte[] address) {
|
||||
return ByteBuffer.allocateDirect(64).put(new byte[64]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getCode(byte[] address) {
|
||||
return ByteBuffer.allocateDirect(64).put(new byte[64]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selfdestruct(byte[] address, byte[] beneficiary) {}
|
||||
|
||||
@Override
|
||||
public ByteBuffer call(ByteBuffer msg) {
|
||||
return ByteBuffer.allocateDirect(64).put(new byte[64]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getTxContext() {
|
||||
return ByteBuffer.allocateDirect(64).put(new byte[64]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getBlockHash(long number) {
|
||||
return ByteBuffer.allocateDirect(64).put(new byte[64]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emitLog(byte[] address, byte[] data, int dataSize, byte[][] topics, int topicCount) {}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
package org.ethereum.evmc;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class TestMessage {
|
||||
int kind;
|
||||
int flags;
|
||||
int depth;
|
||||
long gas;
|
||||
char[] destination;
|
||||
char[] sender;
|
||||
char[] inputData;
|
||||
long inputSize;
|
||||
char[] value;
|
||||
byte[] createSalt;
|
||||
|
||||
public TestMessage(
|
||||
int kind,
|
||||
char[] sender,
|
||||
char[] destination,
|
||||
char[] value,
|
||||
char[] inputData,
|
||||
long gas,
|
||||
int depth) {
|
||||
this.kind = kind;
|
||||
this.flags = 0;
|
||||
this.depth = depth;
|
||||
this.gas = gas;
|
||||
this.destination = destination;
|
||||
this.sender = sender;
|
||||
this.inputData = inputData;
|
||||
this.inputSize = (long) inputData.length;
|
||||
this.value = value;
|
||||
this.createSalt = new byte[32];
|
||||
}
|
||||
|
||||
public TestMessage(ByteBuffer msg) {
|
||||
this.kind = msg.getInt();
|
||||
this.flags = msg.getInt();
|
||||
this.depth = msg.getInt();
|
||||
msg.getInt(); // padding
|
||||
this.gas = msg.getLong();
|
||||
ByteBuffer tmpbuf = msg.get(new byte[20]);
|
||||
this.destination = StandardCharsets.ISO_8859_1.decode(tmpbuf).array();
|
||||
tmpbuf = msg.get(new byte[20]);
|
||||
this.sender = StandardCharsets.ISO_8859_1.decode(tmpbuf).array();
|
||||
tmpbuf = msg.get(new byte[8]);
|
||||
this.inputData = StandardCharsets.ISO_8859_1.decode(tmpbuf).array();
|
||||
this.inputSize = msg.getLong();
|
||||
tmpbuf = msg.get(new byte[32]);
|
||||
this.value = StandardCharsets.ISO_8859_1.decode(tmpbuf).array();
|
||||
this.createSalt = msg.get(new byte[32]).array();
|
||||
}
|
||||
|
||||
public ByteBuffer toByteBuffer() {
|
||||
|
||||
return ByteBuffer.allocateDirect(152)
|
||||
.order(ByteOrder.nativeOrder())
|
||||
.putInt(kind) // 4
|
||||
.putInt(flags) // 4
|
||||
.putInt(depth) // 4
|
||||
.put(new byte[4]) // 4 (padding)
|
||||
.putLong(gas) // 8
|
||||
.put(StandardCharsets.ISO_8859_1.encode(CharBuffer.wrap(destination))) // 20
|
||||
.put(StandardCharsets.ISO_8859_1.encode(CharBuffer.wrap(sender))) // 20
|
||||
.put(StandardCharsets.ISO_8859_1.encode(CharBuffer.wrap(inputData))) // 8
|
||||
.putLong(inputSize) // 8
|
||||
.put(StandardCharsets.ISO_8859_1.encode(CharBuffer.wrap(value))) // 32
|
||||
.put(createSalt); // 32
|
||||
}
|
||||
}
|
2
bindings/java/settings.gradle
Normal file
2
bindings/java/settings.gradle
Normal file
@ -0,0 +1,2 @@
|
||||
rootProject.name = 'evmc'
|
||||
include 'java'
|
4
bindings/java/wrapper.gradle
Normal file
4
bindings/java/wrapper.gradle
Normal file
@ -0,0 +1,4 @@
|
||||
task setup(type: Wrapper) {
|
||||
description = "Download the gradle wrapper and requisite files. Overwrites existing wrapper files."
|
||||
gradleVersion = "5.0"
|
||||
}
|
22
circle.yml
22
circle.yml
@ -64,7 +64,7 @@ jobs:
|
||||
name: "Check code format"
|
||||
command: |
|
||||
clang-format --version
|
||||
find examples include lib test -name '*.hpp' -o -name '*.cpp' -o -name '*.h' -o -name '*.c' | xargs clang-format -i
|
||||
find bindings/java examples include lib test -name '*.hpp' -o -name '*.cpp' -o -name '*.h' -o -name '*.c' | xargs clang-format -i
|
||||
git diff --color --exit-code
|
||||
- run:
|
||||
name: "Run codespell"
|
||||
@ -182,6 +182,25 @@ jobs:
|
||||
- image: circleci/golang:1.9
|
||||
steps: *bindings-go-steps
|
||||
|
||||
bindings-java:
|
||||
docker:
|
||||
- image: circleci/openjdk:11-stretch
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Update environment
|
||||
command: |
|
||||
sudo apt -qq update
|
||||
sudo apt -yq install build-essential
|
||||
- run:
|
||||
name: "Java Build"
|
||||
command: |
|
||||
cd bindings/java && make clean build
|
||||
- run:
|
||||
name: "Java Test"
|
||||
command: |
|
||||
cd bindings/java && make test
|
||||
|
||||
bindings-rust:
|
||||
docker:
|
||||
- image: rust:1
|
||||
@ -261,6 +280,7 @@ workflows:
|
||||
- build-gcc-32bit
|
||||
- bindings-go-latest
|
||||
- bindings-go-min
|
||||
- bindings-java
|
||||
- bindings-rust:
|
||||
requires:
|
||||
- build-gcc8-cxx17
|
||||
|
Loading…
x
Reference in New Issue
Block a user