diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 2aaea06..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-/Java/bin/
-/Java/target/
-/Java/.settings/
diff --git a/Java/.classpath b/Java/.classpath
deleted file mode 100644
index f3d3455..0000000
--- a/Java/.classpath
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Java/.gitignore b/Java/.gitignore
new file mode 100644
index 0000000..e941587
--- /dev/null
+++ b/Java/.gitignore
@@ -0,0 +1,4 @@
+target/
+.settings*
+.classpath
+.project
diff --git a/Java/.project b/Java/.project
deleted file mode 100644
index 70a0c8c..0000000
--- a/Java/.project
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
- CIYAM-AT-Java
-
-
-
-
-
- org.eclipse.jdt.core.javabuilder
-
-
-
-
- org.eclipse.m2e.core.maven2Builder
-
-
-
-
-
- org.eclipse.m2e.core.maven2Nature
- org.eclipse.jdt.core.javanature
-
-
diff --git a/Java/maven-import.txt b/Java/maven-import.txt
new file mode 100644
index 0000000..160a427
--- /dev/null
+++ b/Java/maven-import.txt
@@ -0,0 +1,11 @@
+# How to import CIYAM AT JAR into your project
+
+# Assumes:
+# your project is called MY-PROJECT
+# your project has local repository in MY-PROJECT/lib/
+# CIYAM AT JAR pathname is in ${CIYAM_AT_JAR}
+
+CIYAM_AT_VERSION=1.2
+CIYAM_AT_JAR=../CIYAM-AT/Java/target/AT-${CIYAM_AT_VERSION}.jar
+cd MY-PROJECT
+mvn install:install-file -DlocalRepositoryPath=lib/ -Dfile=${CIYAM_AT_JAR} -DgroupId=org.ciyam -DartifactId=at -Dpackaging=jar -Dversion=${CIYAM_AT_VERSION}
diff --git a/Java/pom.xml b/Java/pom.xml
index 96a3131..c207635 100644
--- a/Java/pom.xml
+++ b/Java/pom.xml
@@ -1,28 +1,42 @@
-
- 4.0.0
- CIYAM-AT-Java
- CIYAM-AT-Java
- 1.0
-
- src
- tests
-
-
- maven-compiler-plugin
- 3.5.1
-
-
- 1.8
-
-
-
-
-
-
- org.bouncycastle
- bcprov-jdk15on
- 1.60
- test
-
-
+
+ 4.0.0
+ org.ciyam
+ AT
+ 1.2
+ jar
+
+ true
+ 1.64
+
+
+ src/main/java
+ src/test/java
+
+
+ maven-compiler-plugin
+ 3.8.0
+
+ 11
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.0.0-M4
+
+ ${skipTests}
+
+
+
+
+
+
+ org.bouncycastle
+ bcprov-jdk15on
+ ${bouncycastle.version}
+ test
+
+
\ No newline at end of file
diff --git a/Java/src/org/ciyam/at/API.java b/Java/src/main/java/org/ciyam/at/API.java
similarity index 57%
rename from Java/src/org/ciyam/at/API.java
rename to Java/src/main/java/org/ciyam/at/API.java
index 5c45d72..07bd3e4 100644
--- a/Java/src/org/ciyam/at/API.java
+++ b/Java/src/main/java/org/ciyam/at/API.java
@@ -1,5 +1,12 @@
package org.ciyam.at;
+import static java.util.Arrays.stream;
+import static java.util.stream.Collectors.toMap;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Map;
+
/**
* API for CIYAM AT "Function Codes" for blockchain-specific interactions.
*
@@ -12,6 +19,27 @@ package org.ciyam.at;
*/
public abstract class API {
+ /** Suggested transaction types to be used by the AT sub-system */
+ public enum ATTransactionType {
+ PAYMENT(0),
+ MESSAGE(1);
+
+ public final long value;
+
+ private static final Map map = stream(ATTransactionType.values()).collect(toMap(type -> type.value, type -> type));
+
+ ATTransactionType(long value) {
+ this.value = value;
+ }
+
+ public static ATTransactionType valueOf(long value) {
+ return map.get(value);
+ }
+ }
+
+ /** Returns maximum number of permitted steps per execution round */
+ public abstract int getMaxStepsPerRound();
+
/** Returns fee for executing opcode in terms of execution "steps" */
public abstract int getOpCodeSteps(OpCode opcode);
@@ -32,7 +60,7 @@ public abstract class API {
/** Put previous block's signature hash in A */
public abstract void putPreviousBlockHashInA(MachineState state);
- /** Put next transaction to AT after timestamp in A */
+ /** Put next transaction to AT after timestamp in A, or zero A if no more transactions */
public abstract void putTransactionAfterTimestampInA(Timestamp timestamp, MachineState state);
/** Return type from transaction in A, or 0xffffffffffffffff if A not valid transaction */
@@ -56,7 +84,7 @@ public abstract class API {
*/
public abstract long generateRandomUsingTransactionInA(MachineState state);
- /** Put 'message' from transaction in A into B */
+ /** Put 'message' from transaction in A into B, or zero B if not a message transaction */
public abstract void putMessageFromTransactionInAIntoB(MachineState state);
/** Put sender/creator address from transaction in A into B */
@@ -81,14 +109,20 @@ public abstract class API {
*/
public abstract long addMinutesToTimestamp(Timestamp timestamp, long minutes, MachineState state);
- /** AT has finished. Return remaining funds to creator */
+ /**
+ * AT has finished. Return remaining funds to creator.
+ *
+ * @param amount
+ * - final balance to be returned to creator
+ * @param state
+ */
public abstract void onFinished(long amount, MachineState state);
/** AT has encountered fatal error */
public abstract void onFatalError(MachineState state, ExecutionException e);
/** Pre-execute checking of param requirements for platform-specific functions */
- public abstract void platformSpecificPreExecuteCheck(short functionCodeValue, int paramCount, boolean returnValueExpected)
+ public abstract void platformSpecificPreExecuteCheck(int paramCount, boolean returnValueExpected, MachineState state, short rawFunctionCode)
throws IllegalFunctionCodeException;
/**
@@ -96,7 +130,7 @@ public abstract class API {
*
* @throws ExecutionException
*/
- public abstract void platformSpecificPostCheckExecute(short functionCodeValue, FunctionData functionData, MachineState state) throws ExecutionException;
+ public abstract void platformSpecificPostCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException;
/** Convenience method to allow subclasses to access package-scoped MachineState.setIsSleeping */
protected void setIsSleeping(MachineState state, boolean isSleeping) {
@@ -108,37 +142,90 @@ public abstract class API {
return state.isFirstOpCodeAfterSleeping();
}
- /** Convenience methods to allow subclasses to access package-scoped a1-a4, b1-b4 variables */
- protected void setA1(MachineState state, long value) {
+ /** Convenience method to allow subclasses to access MachineState.rewindCodePosition */
+ protected void rewindCodePosition(MachineState state, int offset) {
+ state.rewindCodePosition(offset);
+ }
+
+ protected void setSleepUntilHeight(MachineState state, int height) {
+ state.setSleepUntilHeight(height);
+ }
+
+ /* Convenience methods to allow subclasses to access package-scoped a1-a4, b1-b4 variables */
+
+ public void zeroA(MachineState state) {
+ state.a1 = 0L;
+ state.a2 = 0L;
+ state.a3 = 0L;
+ state.a4 = 0L;
+ }
+
+ public void zeroB(MachineState state) {
+ state.b1 = 0L;
+ state.b2 = 0L;
+ state.b3 = 0L;
+ state.b4 = 0L;
+ }
+
+ public void setAToMaxValue(MachineState state) {
+ state.a1 = 0xffffffffffffffffL;
+ state.a2 = 0xffffffffffffffffL;
+ state.a3 = 0xffffffffffffffffL;
+ state.a4 = 0xffffffffffffffffL;
+ }
+
+ public void setA1(MachineState state, long value) {
state.a1 = value;
}
- protected void setA2(MachineState state, long value) {
+ public void setA2(MachineState state, long value) {
state.a2 = value;
}
- protected void setA3(MachineState state, long value) {
+ public void setA3(MachineState state, long value) {
state.a3 = value;
}
- protected void setA4(MachineState state, long value) {
+ public void setA4(MachineState state, long value) {
state.a4 = value;
}
- protected void setB1(MachineState state, long value) {
+ public void setA(MachineState state, byte[] bytes) {
+ // Enforce endian
+ ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
+ byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
+
+ state.a1 = byteBuffer.getLong();
+ state.a2 = byteBuffer.getLong();
+ state.a3 = byteBuffer.getLong();
+ state.a4 = byteBuffer.getLong();
+ }
+
+ public void setB1(MachineState state, long value) {
state.b1 = value;
}
- protected void setB2(MachineState state, long value) {
+ public void setB2(MachineState state, long value) {
state.b2 = value;
}
- protected void setB3(MachineState state, long value) {
+ public void setB3(MachineState state, long value) {
state.b3 = value;
}
- protected void setB4(MachineState state, long value) {
+ public void setB4(MachineState state, long value) {
state.b4 = value;
}
+ public void setB(MachineState state, byte[] bytes) {
+ // Enforce endian
+ ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
+ byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
+
+ state.b1 = byteBuffer.getLong();
+ state.b2 = byteBuffer.getLong();
+ state.b3 = byteBuffer.getLong();
+ state.b4 = byteBuffer.getLong();
+ }
+
}
diff --git a/Java/src/org/ciyam/at/CodeSegmentException.java b/Java/src/main/java/org/ciyam/at/CodeSegmentException.java
similarity index 100%
rename from Java/src/org/ciyam/at/CodeSegmentException.java
rename to Java/src/main/java/org/ciyam/at/CodeSegmentException.java
diff --git a/Java/src/org/ciyam/at/ExecutionException.java b/Java/src/main/java/org/ciyam/at/ExecutionException.java
similarity index 100%
rename from Java/src/org/ciyam/at/ExecutionException.java
rename to Java/src/main/java/org/ciyam/at/ExecutionException.java
diff --git a/Java/src/org/ciyam/at/FunctionCode.java b/Java/src/main/java/org/ciyam/at/FunctionCode.java
similarity index 98%
rename from Java/src/org/ciyam/at/FunctionCode.java
rename to Java/src/main/java/org/ciyam/at/FunctionCode.java
index 0bf8263..1a5e1bf 100644
--- a/Java/src/org/ciyam/at/FunctionCode.java
+++ b/Java/src/main/java/org/ciyam/at/FunctionCode.java
@@ -474,8 +474,8 @@ public enum FunctionCode {
state.b1 = digestByteBuffer.getLong();
state.b2 = digestByteBuffer.getLong();
- state.b3 = 0L; // XXX Or do we leave B3 untouched?
- state.b4 = 0L; // XXX Or do we leave B4 untouched?
+ state.b3 = 0L;
+ state.b4 = 0L;
} catch (NoSuchAlgorithmException e) {
throw new ExecutionException("No MD5 message digest service available", e);
}
@@ -544,7 +544,7 @@ public enum FunctionCode {
state.b1 = digestByteBuffer.getLong();
state.b2 = digestByteBuffer.getLong();
state.b3 = (long) digestByteBuffer.getInt() & 0xffffffffL;
- state.b4 = 0L; // XXX Or do we leave B4 untouched?
+ state.b4 = 0L;
} catch (NoSuchAlgorithmException e) {
throw new ExecutionException("No RIPEMD160 message digest service available", e);
}
@@ -577,7 +577,7 @@ public enum FunctionCode {
digestByteBuffer.putLong(state.b1);
digestByteBuffer.putLong(state.b2);
digestByteBuffer.putInt((int) (state.b3 & 0xffffffffL));
- // XXX: b4 ignored
+ // NOTE: b4 ignored
byte[] expectedDigest = digestByteBuffer.array();
@@ -762,8 +762,7 @@ public enum FunctionCode {
// If API set isSleeping then rewind program counter (actually codeByteBuffer) ready for being awoken
if (state.getIsSleeping()) {
// EXT_FUN_RET(1) + our function code(2) + address(4)
- int newPosition = state.codeByteBuffer.position() - MachineState.OPCODE_SIZE - MachineState.FUNCTIONCODE_SIZE - MachineState.ADDRESS_SIZE;
- state.codeByteBuffer.position(newPosition);
+ state.rewindCodePosition(MachineState.OPCODE_SIZE + MachineState.FUNCTIONCODE_SIZE + MachineState.ADDRESS_SIZE);
// If specific sleep height not set, default to next block
if (state.getSleepUntilHeight() == null)
@@ -910,12 +909,12 @@ public enum FunctionCode {
API_PASSTHROUGH(0x0500, 0, false) {
@Override
public void preExecuteCheck(int paramCount, boolean returnValueExpected, MachineState state, short rawFunctionCode) throws ExecutionException {
- state.getAPI().platformSpecificPreExecuteCheck(rawFunctionCode, paramCount, returnValueExpected);
+ state.getAPI().platformSpecificPreExecuteCheck(paramCount, returnValueExpected, state, rawFunctionCode);
}
@Override
protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException {
- state.getAPI().platformSpecificPostCheckExecute(rawFunctionCode, functionData, state);
+ state.getAPI().platformSpecificPostCheckExecute(functionData, state, rawFunctionCode);
}
};
diff --git a/Java/src/org/ciyam/at/FunctionData.java b/Java/src/main/java/org/ciyam/at/FunctionData.java
similarity index 100%
rename from Java/src/org/ciyam/at/FunctionData.java
rename to Java/src/main/java/org/ciyam/at/FunctionData.java
diff --git a/Java/src/org/ciyam/at/IllegalFunctionCodeException.java b/Java/src/main/java/org/ciyam/at/IllegalFunctionCodeException.java
similarity index 100%
rename from Java/src/org/ciyam/at/IllegalFunctionCodeException.java
rename to Java/src/main/java/org/ciyam/at/IllegalFunctionCodeException.java
diff --git a/Java/src/org/ciyam/at/IllegalOperationException.java b/Java/src/main/java/org/ciyam/at/IllegalOperationException.java
similarity index 100%
rename from Java/src/org/ciyam/at/IllegalOperationException.java
rename to Java/src/main/java/org/ciyam/at/IllegalOperationException.java
diff --git a/Java/src/org/ciyam/at/InvalidAddressException.java b/Java/src/main/java/org/ciyam/at/InvalidAddressException.java
similarity index 100%
rename from Java/src/org/ciyam/at/InvalidAddressException.java
rename to Java/src/main/java/org/ciyam/at/InvalidAddressException.java
diff --git a/Java/src/org/ciyam/at/LoggerInterface.java b/Java/src/main/java/org/ciyam/at/LoggerInterface.java
similarity index 100%
rename from Java/src/org/ciyam/at/LoggerInterface.java
rename to Java/src/main/java/org/ciyam/at/LoggerInterface.java
diff --git a/Java/src/org/ciyam/at/MachineState.java b/Java/src/main/java/org/ciyam/at/MachineState.java
similarity index 83%
rename from Java/src/org/ciyam/at/MachineState.java
rename to Java/src/main/java/org/ciyam/at/MachineState.java
index 0883ca0..4bf7866 100644
--- a/Java/src/org/ciyam/at/MachineState.java
+++ b/Java/src/main/java/org/ciyam/at/MachineState.java
@@ -11,7 +11,8 @@ import java.util.Map;
public class MachineState {
/** Header bytes length */
- public static final int HEADER_LENGTH = 2 + 2 + 2 + 2 + 2 + 2; // version reserved code data call-stack user-stack
+ // version + reserved + code + data + call-stack + user-stack + min-activation-amount
+ public static final int HEADER_LENGTH = 2 + 2 + 2 + 2 + 2 + 2 + 8;
/** Size of one OpCode - typically 1 byte (byte) */
public static final int OPCODE_SIZE = 1;
@@ -28,9 +29,6 @@ public class MachineState {
/** Maximum value for an address in the code segment */
public static final int MAX_CODE_ADDRESS = 0x0000ffff;
- /** Maximum number of steps per execution round */
- public static final int MAX_STEPS = 500;
-
private static class VersionedConstants {
/** Bytes per code page */
public final int CODE_PAGE_SIZE;
@@ -50,10 +48,10 @@ public class MachineState {
}
/** Map of constants (e.g. CODE_PAGE_SIZE) by AT version */
- private static final Map VERSIONED_CONSTANTS = new HashMap();
+ private static final Map VERSIONED_CONSTANTS = new HashMap<>();
static {
VERSIONED_CONSTANTS.put((short) 1, new VersionedConstants(256, 256, 256, 256));
- VERSIONED_CONSTANTS.put((short) 3, new VersionedConstants(OPCODE_SIZE, VALUE_SIZE, ADDRESS_SIZE, VALUE_SIZE));
+ VERSIONED_CONSTANTS.put((short) 2, new VersionedConstants(OPCODE_SIZE, VALUE_SIZE, ADDRESS_SIZE, VALUE_SIZE));
}
// Set during construction
@@ -63,6 +61,7 @@ public class MachineState {
public final short numDataPages;
public final short numCallStackPages;
public final short numUserStackPages;
+ public final long minActivationAmount;
private final byte[] headerBytes;
@@ -148,7 +147,7 @@ public class MachineState {
this.version = byteBuffer.getShort();
if (this.version < 1)
- throw new IllegalArgumentException("Version must be >= 0");
+ throw new IllegalArgumentException("Version must be > 0");
this.constants = VERSIONED_CONSTANTS.get(this.version);
if (this.constants == null)
@@ -172,6 +171,8 @@ public class MachineState {
if (this.numUserStackPages < 0)
throw new IllegalArgumentException("Number of user stack pages must be >= 0");
+ this.minActivationAmount = byteBuffer.getLong();
+
// Header OK - set up code and data buffers
this.codeByteBuffer = ByteBuffer.allocate(this.numCodePages * this.constants.CODE_PAGE_SIZE).order(ByteOrder.LITTLE_ENDIAN);
this.dataByteBuffer = ByteBuffer.allocate(this.numDataPages * this.constants.DATA_PAGE_SIZE).order(ByteOrder.LITTLE_ENDIAN);
@@ -207,7 +208,7 @@ public class MachineState {
commonFinalConstruction();
}
- /** For creating a new machine state */
+ /** For creating a new machine state - used in tests */
public MachineState(API api, LoggerInterface logger, byte[] headerBytes, byte[] codeBytes, byte[] dataBytes) {
this(api, logger, headerBytes);
@@ -236,6 +237,14 @@ public class MachineState {
this.isFinished = false;
this.hadFatalError = false;
this.previousBalance = 0;
+
+ // If we have a minimum activation amount then create AT in frozen state, requiring that amount to unfreeze.
+ // If creator also sends funds with creation then AT will unfreeze on first call.
+ if (this.minActivationAmount > 0) {
+ this.isFrozen = true;
+ // -1 because current balance has to exceed frozenBalance to unfreeze AT
+ this.frozenBalance = this.minActivationAmount - 1;
+ }
}
// Getters / setters
@@ -275,8 +284,8 @@ public class MachineState {
return this.sleepUntilHeight;
}
- /* package */ void setSleepUntilHeight(Integer address) {
- this.sleepUntilHeight = address;
+ /* package */ void setSleepUntilHeight(Integer height) {
+ this.sleepUntilHeight = height;
}
public boolean getIsStopped() {
@@ -319,7 +328,6 @@ public class MachineState {
this.hadFatalError = hadFatalError;
}
- // No corresponding setters due to package-scope - see above
public long getA1() {
return this.a1;
}
@@ -336,6 +344,18 @@ public class MachineState {
return this.a4;
}
+ public byte[] getA() {
+ ByteBuffer byteBuffer = ByteBuffer.allocate(4 * 8);
+ byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
+
+ byteBuffer.putLong(this.a1);
+ byteBuffer.putLong(this.a2);
+ byteBuffer.putLong(this.a3);
+ byteBuffer.putLong(this.a4);
+
+ return byteBuffer.array();
+ }
+
public long getB1() {
return this.b1;
}
@@ -351,7 +371,18 @@ public class MachineState {
public long getB4() {
return this.b4;
}
- // End of package-scope pseudo-registers
+
+ public byte[] getB() {
+ ByteBuffer byteBuffer = ByteBuffer.allocate(4 * 8);
+ byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
+
+ byteBuffer.putLong(this.b1);
+ byteBuffer.putLong(this.b2);
+ byteBuffer.putLong(this.b3);
+ byteBuffer.putLong(this.b4);
+
+ return byteBuffer.array();
+ }
public int getCurrentBlockHeight() {
return this.currentBlockHeight;
@@ -389,8 +420,80 @@ public class MachineState {
return this.isFirstOpCodeAfterSleeping;
}
+ /**
+ * Rewinds program counter by amount.
+ *
+ * Actually rewinds codeByteBuffer's position, not PC, as the later is synchronized from the former after each OpCode is executed.
+ *
+ * @param offset
+ */
+ /* package */ void rewindCodePosition(int offset) {
+ this.codeByteBuffer.position(this.codeByteBuffer.position() - offset);
+ }
+
// Serialization
+ public static byte[] toCreationBytes(short version, byte[] codeBytes, byte[] dataBytes, short numCallStackPages, short numUserStackPages, long minActivationAmount) {
+ if (version < 1)
+ throw new IllegalArgumentException("Version must be > 0");
+
+ VersionedConstants constants = VERSIONED_CONSTANTS.get(version);
+ if (constants == null)
+ throw new IllegalArgumentException("Version " + version + " unsupported");
+
+ // Calculate number of code pages
+ if (codeBytes.length == 0)
+ throw new IllegalArgumentException("Empty code bytes");
+ short numCodePages = (short) (((codeBytes.length - 1) / constants.CODE_PAGE_SIZE) + 1);
+
+ // Calculate number of data pages
+ if (dataBytes.length == 0)
+ throw new IllegalArgumentException("Empty data bytes");
+ short numDataPages = (short) (((dataBytes.length - 1) / constants.DATA_PAGE_SIZE) + 1);
+
+ int creationBytesLength = HEADER_LENGTH + numCodePages * constants.CODE_PAGE_SIZE + numDataPages + constants.DATA_PAGE_SIZE;
+ byte[] creationBytes = new byte[creationBytesLength];
+
+ ByteBuffer byteBuffer = ByteBuffer.wrap(creationBytes);
+ byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
+
+ // Header bytes:
+
+ // Version
+ byteBuffer.putShort(version);
+
+ // Reserved
+ byteBuffer.putShort((short) 0);
+
+ // Code length
+ byteBuffer.putShort(numCodePages);
+
+ // Data length
+ byteBuffer.putShort(numDataPages);
+
+ // Call stack length
+ byteBuffer.putShort(numCallStackPages);
+
+ // User stack length
+ byteBuffer.putShort(numUserStackPages);
+
+ // Minimum activation amount
+ byteBuffer.putLong(minActivationAmount);
+
+ // Code bytes
+ System.arraycopy(codeBytes, 0, creationBytes, HEADER_LENGTH, codeBytes.length);
+
+ // Data bytes
+ System.arraycopy(dataBytes, 0, creationBytes, HEADER_LENGTH + numCodePages * constants.CODE_PAGE_SIZE, dataBytes.length);
+
+ return creationBytes;
+ }
+
+ /** Returns code bytes only as these are read-only so no need to be duplicated in every serialized state */
+ public byte[] getCodeBytes() {
+ return this.codeByteBuffer.array();
+ }
+
/** For serializing a machine state */
public byte[] toBytes() {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
@@ -399,9 +502,6 @@ public class MachineState {
// Header first
bytes.write(this.headerBytes);
- // Code
- bytes.write(this.codeByteBuffer.array());
-
// Data
bytes.write(this.dataByteBuffer.array());
@@ -473,7 +573,7 @@ public class MachineState {
}
/** For restoring a previously serialized machine state */
- public static MachineState fromBytes(API api, LoggerInterface logger, byte[] bytes) {
+ public static MachineState fromBytes(API api, LoggerInterface logger, byte[] bytes, byte[] codeBytes) {
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
byte[] headerBytes = new byte[HEADER_LENGTH];
@@ -481,8 +581,9 @@ public class MachineState {
MachineState state = new MachineState(api, logger, headerBytes);
- byte[] codeBytes = new byte[state.codeByteBuffer.capacity()];
- byteBuffer.get(codeBytes);
+ if (codeBytes.length != state.codeByteBuffer.capacity())
+ throw new IllegalStateException("Passed codeBytes does not match length in header");
+
System.arraycopy(codeBytes, 0, state.codeByteBuffer.array(), 0, codeBytes.length);
byte[] dataBytes = new byte[state.dataByteBuffer.capacity()];
@@ -631,7 +732,9 @@ public class MachineState {
this.isFrozen = false;
this.frozenBalance = null;
+ // Cache useful info from API
long feePerStep = this.api.getFeePerStep();
+ int maxSteps = api.getMaxStepsPerRound();
// Set byte buffer position using program counter
codeByteBuffer.position(this.programCounter);
@@ -650,8 +753,8 @@ public class MachineState {
int opcodeSteps = this.api.getOpCodeSteps(nextOpCode);
long opcodeFee = opcodeSteps * feePerStep;
- if (this.steps + opcodeSteps > MAX_STEPS) {
- logger.debug("Enforced sleep due to exceeding maximum number of steps (" + MAX_STEPS + ") per execution round");
+ if (this.steps + opcodeSteps > maxSteps) {
+ logger.debug("Enforced sleep due to exceeding maximum number of steps (" + maxSteps + ") per execution round");
this.isSleeping = true;
break;
}
@@ -717,7 +820,7 @@ public class MachineState {
/** Return disassembly of code bytes */
public String disassemble() throws ExecutionException {
- String output = "";
+ StringBuilder output = new StringBuilder();
codeByteBuffer.position(0);
@@ -730,13 +833,13 @@ public class MachineState {
if (nextOpCode == null)
throw new IllegalOperationException("OpCode 0x" + String.format("%02x", rawOpCode) + " not recognised");
- if (!output.isEmpty())
- output += "\n";
+ if (output.length() != 0)
+ output.append("\n");
- output += "[PC: " + String.format("%04x", codeByteBuffer.position() - 1) + "] " + nextOpCode.disassemble(codeByteBuffer, dataByteBuffer);
+ output.append(String.format("[PC: %04x] %s", codeByteBuffer.position() - 1,nextOpCode.disassemble(codeByteBuffer, dataByteBuffer)));
}
- return output;
+ return output.toString();
}
}
diff --git a/Java/src/org/ciyam/at/OpCode.java b/Java/src/main/java/org/ciyam/at/OpCode.java
similarity index 98%
rename from Java/src/org/ciyam/at/OpCode.java
rename to Java/src/main/java/org/ciyam/at/OpCode.java
index 3fbfcbb..935201e 100644
--- a/Java/src/org/ciyam/at/OpCode.java
+++ b/Java/src/main/java/org/ciyam/at/OpCode.java
@@ -409,7 +409,7 @@ public enum OpCode {
* @addr1 <<= $addr2
*/
SHL_DAT(0x17, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) {
- private static final long MAX_SHIFT = MachineState.VALUE_SIZE * 8;
+ private static final long MAX_SHIFT = MachineState.VALUE_SIZE * 8L;
@Override
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
@@ -424,7 +424,7 @@ public enum OpCode {
* Note: new MSB bit will be zero
*/
SHR_DAT(0x18, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) {
- private static final long MAX_SHIFT = MachineState.VALUE_SIZE * 8;
+ private static final long MAX_SHIFT = MachineState.VALUE_SIZE * 8L;
@Override
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
@@ -845,7 +845,7 @@ public enum OpCode {
public final OpCodeParam[] params;
// Create a map of opcode values to OpCode
- private final static Map map = Arrays.stream(OpCode.values()).collect(Collectors.toMap(opcode -> opcode.value, opcode -> opcode));
+ private static final Map map = Arrays.stream(OpCode.values()).collect(Collectors.toMap(opcode -> opcode.value, opcode -> opcode));
private OpCode(int value, OpCodeParam... params) {
this.value = (byte) value;
@@ -876,7 +876,7 @@ public enum OpCode {
public abstract void executeWithParams(MachineState state, Object... args) throws ExecutionException;
public void execute(MachineState state) throws ExecutionException {
- List