Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

java: introduce proper types and minimise reliance on ByteBuffer/byte[] #555

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions bindings/java/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ add_jar(evmc-jar
java/src/main/java/org/ethereum/evmc/EvmcVm.java
java/src/main/java/org/ethereum/evmc/Host.java
java/src/main/java/org/ethereum/evmc/HostContext.java
java/src/main/java/org/ethereum/evmc/StorageStatus.java
java/src/main/java/org/ethereum/evmc/types/Address.java
java/src/main/java/org/ethereum/evmc/types/Bytes32.java
java/src/main/java/org/ethereum/evmc/types/FixedLengthType.java
java/src/main/java/org/ethereum/evmc/types/Message.java
java/src/main/java/org/ethereum/evmc/types/TxContext.java
java/src/main/java/org/ethereum/evmc/types/Uint256.java
GENERATE_NATIVE_HEADERS evmc-java-native-headers)

add_library(evmc-java SHARED)
Expand Down
13 changes: 8 additions & 5 deletions bindings/java/java/src/main/java/org/ethereum/evmc/EvmcVm.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import org.ethereum.evmc.types.*;

/**
* The Java interface to the evm instance.
Expand Down Expand Up @@ -143,17 +144,19 @@ public String version() {
*
* <p>This is a mandatory method and MUST NOT be set to NULL.
*/
private static native ByteBuffer execute(
ByteBuffer nativeVm, HostContext context, int rev, ByteBuffer msg, ByteBuffer code);
private static native void execute(
ByteBuffer nativeVm, HostContext context, int rev, Message msg, ByteBuffer code, Result 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) {
return execute(nativeVm, context, rev, msg, code);
public synchronized Result execute(
HostContext context, int rev, Message msg, ByteBuffer code) {
Result result;
execute(nativeVm, context, rev, msg, code, result);
return result;
}

/**
Expand Down
36 changes: 23 additions & 13 deletions bindings/java/java/src/main/java/org/ethereum/evmc/Host.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
package org.ethereum.evmc;

import java.nio.ByteBuffer;
import java.util.Arrays;
import org.ethereum.evmc.types.*;
import org.ethereum.evmc.types.Address;
import org.ethereum.evmc.types.Bytes32;

/**
* The Host interface.
Expand All @@ -21,56 +25,59 @@ private static ByteBuffer ensureDirectBuffer(ByteBuffer input) {

/** Check account existence callback function. */
static boolean account_exists(HostContext context, byte[] address) {
return context.accountExists(address);
return context.accountExists(new Address(address));
}

/** Get storage callback function. */
static ByteBuffer get_storage(HostContext context, byte[] address, byte[] key) {
return ensureDirectBuffer(context.getStorage(address, key));
return context.getStorage(new Address(address), new Bytes32(key)).getByteBuffer();
}

/** Set storage callback function. */
static int set_storage(HostContext context, byte[] address, byte[] key, byte[] value) {
return context.setStorage(address, key, value);
StorageStatus status =
context.setStorage(new Address(address), new Bytes32(key), new Bytes32(value));
return status.code;
}

/** Get balance callback function. */
static ByteBuffer get_balance(HostContext context, byte[] address) {
return ensureDirectBuffer(context.getBalance(address));
return context.getBalance(new Address(address)).getByteBuffer();
}

/** Get code size callback function. */
static int get_code_size(HostContext context, byte[] address) {
return context.getCodeSize(address);
return context.getCodeSize(new Address(address));
}

/** Get code hash callback function. */
static ByteBuffer get_code_hash(HostContext context, byte[] address) {
return ensureDirectBuffer(context.getCodeHash(address));
return context.getCodeHash(new Address(address)).getByteBuffer();
}

/** Copy code callback function. */
static ByteBuffer copy_code(HostContext context, byte[] address) {
return ensureDirectBuffer(context.getCode(address));
return ensureDirectBuffer(context.getCode(new Address(address)));
}

/** Selfdestruct callback function. */
static void selfdestruct(HostContext context, byte[] address, byte[] beneficiary) {
context.selfdestruct(address, beneficiary);
context.selfdestruct(new Address(address), new Address(beneficiary));
}

/** Call callback function. */
static ByteBuffer call(HostContext context, ByteBuffer msg) {
return ensureDirectBuffer(context.call(msg));
static Result call(HostContext context, Message msg) {
return context.call(msg);
}

/** Get transaction context callback function. */
static ByteBuffer get_tx_context(HostContext context) {
return ensureDirectBuffer(context.getTxContext());
return context.getTxContext().getByteBuffer();
}

/** Get block hash callback function. */
static ByteBuffer get_block_hash_fn(HostContext context, long number) {
return ensureDirectBuffer(context.getBlockHash(number));
return context.getBlockHash(number).getByteBuffer();
}

/** Emit log callback function. */
Expand All @@ -81,7 +88,10 @@ static void emit_log(
int data_size,
byte[][] topics,
int topic_count) {
context.emitLog(address, data, data_size, topics, topic_count);
context.emitLog(
new Address(address),
data,
Arrays.stream(topics).map(Bytes32::new).toArray(Bytes32[]::new));
}

/** Access account callback function. */
Expand Down
27 changes: 13 additions & 14 deletions bindings/java/java/src/main/java/org/ethereum/evmc/HostContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package org.ethereum.evmc;

import java.nio.ByteBuffer;
import org.ethereum.evmc.types.*;

/**
* This interface represents the callback functions must be implemented in order to interface with
Expand All @@ -18,7 +19,7 @@ public interface HostContext {
* @param address The address of the account the query is about.
* @return true if exists, false otherwise.
*/
boolean accountExists(byte[] address);
boolean accountExists(Address address);

/**
* Access account function.
Expand Down Expand Up @@ -54,7 +55,7 @@ public interface HostContext {
* @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);
Bytes32 getStorage(Address address, Bytes32 key);

/**
* Set storage function.
Expand All @@ -69,7 +70,7 @@ public interface HostContext {
* @param value The value to be stored.
* @return The effect on the storage item.
*/
int setStorage(byte[] address, byte[] key, byte[] value);
StorageStatus setStorage(Address address, Bytes32 key, Bytes32 value);

/**
* Get balance function.
Expand All @@ -79,7 +80,7 @@ public interface HostContext {
* @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);
Uint256 getBalance(Address address);

/**
* Get code size function.
Expand All @@ -90,7 +91,7 @@ public interface HostContext {
* @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);
int getCodeSize(Address address);

/**
* Get code hash function.
Expand All @@ -102,7 +103,7 @@ public interface HostContext {
* @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);
Bytes32 getCodeHash(Address address);

/**
* Copy code function.
Expand All @@ -115,7 +116,7 @@ public interface HostContext {
* @param address The address of the account.
* @return A copy of the requested code.
*/
ByteBuffer getCode(byte[] address);
ByteBuffer getCode(Address address);

/**
* Selfdestruct function.
Expand All @@ -126,15 +127,15 @@ public interface HostContext {
* @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);
void selfdestruct(Address address, Address beneficiary);

/**
* This function supports EVM calls.
*
* @param msg The call parameters.
* @return The result of the call.
*/
ByteBuffer call(ByteBuffer msg);
Result call(Message msg);

/**
* Get transaction context function.
Expand All @@ -143,7 +144,7 @@ public interface HostContext {
*
* @return The transaction context.
*/
ByteBuffer getTxContext();
TxContext getTxContext();

/**
* Get block hash function.
Expand All @@ -155,7 +156,7 @@ public interface HostContext {
* @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);
Bytes32 getBlockHash(long number);

/**
* Log function.
Expand All @@ -165,9 +166,7 @@ public interface HostContext {
*
* @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);
void emitLog(Address address, byte[] data, Bytes32[] topics);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.ethereum.evmc;

public enum StorageStatus {
/** The value of a storage item has been left unchanged: 0 -> 0 and X -> X. */
EVMC_STORAGE_UNCHANGED(0),

/** The value of a storage item has been modified: X -> Y. */
EVMC_STORAGE_MODIFIED(1),

/** A storage item has been modified after being modified before: X -> Y -> Z. */
EVMC_STORAGE_MODIFIED_AGAIN(2),

/** A new storage item has been added: 0 -> X. */
EVMC_STORAGE_ADDED(3),

/** A storage item has been deleted: X -> 0. */
EVMC_STORAGE_DELETED(4);

public final int code;

StorageStatus(int code) {
this.code = code;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// EVMC: Ethereum Client-VM Connector API.
// Copyright 2019-2020 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.
package org.ethereum.evmc.types;

import java.nio.ByteBuffer;

public class Address extends FixedLengthType {
@Override
protected int expectedLength() {
return 20;
}

public Address(byte[] data) {
super(data);
}

public Address(ByteBuffer data) {
super(data);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// EVMC: Ethereum Client-VM Connector API.
// Copyright 2019-2020 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.
package org.ethereum.evmc.types;

public class Bytes32 extends FixedLengthType {

public static final Bytes32 EMPTY = new Bytes32(new byte[32]);

@Override
protected int expectedLength() {
return 32;
}

public Bytes32(byte[] data) {
super(data);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// EVMC: Ethereum Client-VM Connector API.
// Copyright 2019-2020 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.
package org.ethereum.evmc.types;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;

public abstract class FixedLengthType {
protected abstract int expectedLength();

private final byte[] data;

public byte[] get() {
return this.data;
}

public ByteBuffer getByteBuffer() {
// This is big endian by default, which is what we want.
return ByteBuffer.allocateDirect(this.data.length).put(this.data);
}

public FixedLengthType(byte[] data) {
if (data == null) {
throw new IllegalArgumentException("data cannot be null");
}
if (data.length != expectedLength()) {
throw new IllegalArgumentException(
"data length expected to be " + expectedLength() + ", was " + data.length);
}
this.data = data;
}

public FixedLengthType(ByteBuffer data) {
if (data == null) {
throw new IllegalArgumentException("data cannot be null");
}
int length = data.remaining();
if (length != expectedLength()) {
throw new IllegalArgumentException(
"data length expected to be " + expectedLength() + ", was " + length);
}
this.data = new byte[length];
data.order(ByteOrder.BIG_ENDIAN).get(this.data, 0, length);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FixedLengthType that = (FixedLengthType) o;
return Arrays.equals(data, that.data);
}

@Override
public int hashCode() {
return Arrays.hashCode(data);
}
}
Loading