diff --git a/Java/src/org/ciyam/at/API.java b/Java/src/org/ciyam/at/API.java index c012e27..6adecaf 100644 --- a/Java/src/org/ciyam/at/API.java +++ b/Java/src/org/ciyam/at/API.java @@ -10,33 +10,33 @@ package org.ciyam.at; * bits) with the second part being the number of the transaction if applicable (also 32 bits and zero if not applicable). * */ -public interface API { +public abstract class API { /** Returns current blockchain's height */ - public int getCurrentBlockHeight(); + public abstract int getCurrentBlockHeight(); /** Returns block height where AT was created */ - public int getATCreationBlockHeight(MachineState state); + public abstract int getATCreationBlockHeight(MachineState state); /** Returns previous block's height */ - default public int getPreviousBlockHeight() { + public int getPreviousBlockHeight() { return getCurrentBlockHeight() - 1; } /** Put previous block's signature hash in A */ - public void putPreviousBlockHashInA(MachineState state); + public abstract void putPreviousBlockHashInA(MachineState state); /** Put next transaction to AT after timestamp in A */ - public void putTransactionAfterTimestampInA(Timestamp timestamp, MachineState state); + public abstract void putTransactionAfterTimestampInA(Timestamp timestamp, MachineState state); /** Return type from transaction in A, or 0xffffffffffffffff if A not valid transaction */ - public long getTypeFromTransactionInA(MachineState state); + public abstract long getTypeFromTransactionInA(MachineState state); /** Return amount from transaction in A, after transaction fees have been deducted, or 0xffffffffffffffff if A not valid transaction */ - public long getAmountFromTransactionInA(MachineState state); + public abstract long getAmountFromTransactionInA(MachineState state); /** Return timestamp from transaction in A, or 0xffffffffffffffff if A not valid transaction */ - public long getTimestampFromTransactionInA(MachineState state); + public abstract long getTimestampFromTransactionInA(MachineState state); /** * Generate pseudo-random number using transaction in A. @@ -48,53 +48,92 @@ public interface API { *

* Returns 0xffffffffffffffff if A not valid transaction. */ - public long generateRandomUsingTransactionInA(MachineState state); + public abstract long generateRandomUsingTransactionInA(MachineState state); /** Put 'message' from transaction in A into B */ - public void putMessageFromTransactionInAIntoB(MachineState state); + public abstract void putMessageFromTransactionInAIntoB(MachineState state); /** Put sender/creator address from transaction in A into B */ - public void putAddressFromTransactionInAIntoB(MachineState state); + public abstract void putAddressFromTransactionInAIntoB(MachineState state); /** Put AT's creator's address into B */ - public void putCreatorAddressIntoB(MachineState state); + public abstract void putCreatorAddressIntoB(MachineState state); /** Return AT's current balance */ - public long getCurrentBalance(MachineState state); + public abstract long getCurrentBalance(MachineState state); /** Return AT's previous balance at end of last execution round. Does not include any amounts sent to AT since */ - public long getPreviousBalance(MachineState state); + public abstract long getPreviousBalance(MachineState state); /** Pay passed amount, or current balance if necessary, (fee inclusive) to address in B */ - public void payAmountToB(long value1, MachineState state); + public abstract void payAmountToB(long value1, MachineState state); /** Pay AT's current balance to address in B */ - public void payCurrentBalanceToB(MachineState state); + public abstract void payCurrentBalanceToB(MachineState state); /** Pay AT's previous balance to address in B */ - public void payPreviousBalanceToB(MachineState state); + public abstract void payPreviousBalanceToB(MachineState state); /** Send 'message' in A to address in B */ - public void messageAToB(MachineState state); + public abstract void messageAToB(MachineState state); /** * Returns minutes of blocks added to 'timestamp' *

* minutes is converted to rough number of blocks and added to 'timestamp' to create return value. */ - public long addMinutesToTimestamp(Timestamp timestamp, long minutes, MachineState state); + public abstract long addMinutesToTimestamp(Timestamp timestamp, long minutes, MachineState state); /** AT has encountered fatal error. Return remaining funds to creator */ - public void onFatalError(MachineState state, ExecutionException e); + public abstract void onFatalError(MachineState state, ExecutionException e); /** Pre-execute checking of param requirements for platform-specific functions */ - public void platformSpecificPreExecuteCheck(short functionCodeValue, int paramCount, boolean returnValueExpected) throws IllegalFunctionCodeException; + public abstract void platformSpecificPreExecuteCheck(short functionCodeValue, int paramCount, boolean returnValueExpected) + throws IllegalFunctionCodeException; /** * Platform-specific function execution * * @throws ExecutionException */ - public void platformSpecificPostCheckExecute(short functionCodeValue, FunctionData functionData, MachineState state) throws ExecutionException; + public abstract void platformSpecificPostCheckExecute(short functionCodeValue, FunctionData functionData, MachineState state) throws ExecutionException; + + /** Convenience method to allow subclasses to access package-scoped MachineState.setIsSleeping */ + protected void setIsSleeping(MachineState state, boolean isSleeping) { + state.setIsSleeping(isSleeping); + } + + /** Convenience methods to allow subclasses to access package-scoped a1-a4, b1-b4 variables */ + protected void setA1(MachineState state, long value) { + state.a1 = value; + } + + protected void setA2(MachineState state, long value) { + state.a2 = value; + } + + protected void setA3(MachineState state, long value) { + state.a3 = value; + } + + protected void setA4(MachineState state, long value) { + state.a4 = value; + } + + protected void setB1(MachineState state, long value) { + state.b1 = value; + } + + protected void setB2(MachineState state, long value) { + state.b2 = value; + } + + protected void setB3(MachineState state, long value) { + state.b3 = value; + } + + protected void setB4(MachineState state, long value) { + state.b4 = value; + } } diff --git a/Java/src/org/ciyam/at/FunctionCode.java b/Java/src/org/ciyam/at/FunctionCode.java index 1aa3aeb..c3c4e37 100644 --- a/Java/src/org/ciyam/at/FunctionCode.java +++ b/Java/src/org/ciyam/at/FunctionCode.java @@ -31,7 +31,7 @@ public enum FunctionCode { @Override protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException { String message = String.valueOf(functionData.value1); - state.logger.echo(message); + state.getLogger().echo(message); } }, /** @@ -671,7 +671,7 @@ public enum FunctionCode { GET_BLOCK_TIMESTAMP(0x0300, 0, true) { @Override protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException { - functionData.returnValue = Timestamp.toLong(state.api.getCurrentBlockHeight(), 0); + functionData.returnValue = Timestamp.toLong(state.getAPI().getCurrentBlockHeight(), 0); } }, /** @@ -681,7 +681,7 @@ public enum FunctionCode { GET_CREATION_TIMESTAMP(0x0301, 0, true) { @Override protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException { - functionData.returnValue = Timestamp.toLong(state.api.getATCreationBlockHeight(state), 0); + functionData.returnValue = Timestamp.toLong(state.getAPI().getATCreationBlockHeight(state), 0); } }, /** @@ -691,7 +691,7 @@ public enum FunctionCode { GET_PREVIOUS_BLOCK_TIMESTAMP(0x0302, 0, true) { @Override protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException { - functionData.returnValue = Timestamp.toLong(state.api.getPreviousBlockHeight(), 0); + functionData.returnValue = Timestamp.toLong(state.getAPI().getPreviousBlockHeight(), 0); } }, /** @@ -701,7 +701,7 @@ public enum FunctionCode { PUT_PREVIOUS_BLOCK_HASH_IN_A(0x0303, 0, false) { @Override protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException { - state.api.putPreviousBlockHashInA(state); + state.getAPI().putPreviousBlockHashInA(state); } }, /** @@ -712,7 +712,7 @@ public enum FunctionCode { PUT_TX_AFTER_TIMESTAMP_IN_A(0x0304, 1, false) { @Override protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException { - state.api.putTransactionAfterTimestampInA(new Timestamp(functionData.value1), state); + state.getAPI().putTransactionAfterTimestampInA(new Timestamp(functionData.value1), state); } }, /** @@ -723,7 +723,7 @@ public enum FunctionCode { GET_TYPE_FROM_TX_IN_A(0x0305, 0, true) { @Override protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException { - functionData.returnValue = state.api.getTypeFromTransactionInA(state); + functionData.returnValue = state.getAPI().getTypeFromTransactionInA(state); } }, /** @@ -734,7 +734,7 @@ public enum FunctionCode { GET_AMOUNT_FROM_TX_IN_A(0x0306, 0, true) { @Override protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException { - functionData.returnValue = state.api.getAmountFromTransactionInA(state); + functionData.returnValue = state.getAPI().getAmountFromTransactionInA(state); } }, /** @@ -745,7 +745,7 @@ public enum FunctionCode { GET_TIMESTAMP_FROM_TX_IN_A(0x0307, 0, true) { @Override protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException { - functionData.returnValue = state.api.getTimestampFromTransactionInA(state); + functionData.returnValue = state.getAPI().getTimestampFromTransactionInA(state); } }, /** @@ -757,15 +757,17 @@ public enum FunctionCode { GENERATE_RANDOM_USING_TX_IN_A(0x0308, 0, true) { @Override protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException { - functionData.returnValue = state.api.generateRandomUsingTransactionInA(state); + functionData.returnValue = state.getAPI().generateRandomUsingTransactionInA(state); - // If API set isSleeping then rewind program counter ready for being awoken - if (state.isSleeping) { - state.programCounter -= 1 + 2 + 4; // EXT_FUN_RET(1) + our function code(2) + address(4) + // 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); // If specific sleep height not set, default to next block - if (state.sleepUntilHeight == null) - state.sleepUntilHeight = state.currentBlockHeight + 1; + if (state.getSleepUntilHeight() == null) + state.setSleepUntilHeight(state.getCurrentBlockHeight() + 1); } } }, @@ -778,7 +780,7 @@ public enum FunctionCode { PUT_MESSAGE_FROM_TX_IN_A_INTO_B(0x0309, 0, false) { @Override protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException { - state.api.putMessageFromTransactionInAIntoB(state); + state.getAPI().putMessageFromTransactionInAIntoB(state); } }, /** @@ -788,7 +790,7 @@ public enum FunctionCode { PUT_ADDRESS_FROM_TX_IN_A_INTO_B(0x030a, 0, false) { @Override protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException { - state.api.putAddressFromTransactionInAIntoB(state); + state.getAPI().putAddressFromTransactionInAIntoB(state); } }, /** @@ -798,7 +800,7 @@ public enum FunctionCode { PUT_CREATOR_INTO_B(0x030b, 0, false) { @Override protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException { - state.api.putCreatorAddressIntoB(state); + state.getAPI().putCreatorAddressIntoB(state); } }, /** @@ -808,7 +810,7 @@ public enum FunctionCode { GET_CURRENT_BALANCE(0x0400, 0, true) { @Override protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException { - functionData.returnValue = state.api.getCurrentBalance(state); + functionData.returnValue = state.getAPI().getCurrentBalance(state); } }, /** @@ -819,7 +821,7 @@ public enum FunctionCode { GET_PREVIOUS_BALANCE(0x0401, 0, true) { @Override protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException { - functionData.returnValue = state.api.getPreviousBalance(state); + functionData.returnValue = state.getAPI().getPreviousBalance(state); } }, /** @@ -830,7 +832,7 @@ public enum FunctionCode { PAY_TO_ADDRESS_IN_B(0x0402, 1, false) { @Override protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException { - state.api.payAmountToB(functionData.value1, state); + state.getAPI().payAmountToB(functionData.value1, state); } }, /** @@ -840,7 +842,7 @@ public enum FunctionCode { PAY_ALL_TO_ADDRESS_IN_B(0x0403, 0, false) { @Override protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException { - state.api.payCurrentBalanceToB(state); + state.getAPI().payCurrentBalanceToB(state); } }, /** @@ -851,7 +853,7 @@ public enum FunctionCode { PAY_PREVIOUS_TO_ADDRESS_IN_B(0x0404, 0, false) { @Override protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException { - state.api.payPreviousBalanceToB(state); + state.getAPI().payPreviousBalanceToB(state); } }, /** @@ -861,7 +863,7 @@ public enum FunctionCode { MESSAGE_A_TO_ADDRESS_IN_B(0x0405, 0, false) { @Override protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException { - state.api.messageAToB(state); + state.getAPI().messageAToB(state); } }, /** @@ -871,7 +873,7 @@ public enum FunctionCode { ADD_MINUTES_TO_TIMESTAMP(0x0406, 2, true) { @Override protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException { - functionData.returnValue = state.api.addMinutesToTimestamp(new Timestamp(functionData.value1), functionData.value2, state); + functionData.returnValue = state.getAPI().addMinutesToTimestamp(new Timestamp(functionData.value1), functionData.value2, state); } }, /** @@ -882,12 +884,12 @@ public enum FunctionCode { API_PASSTHROUGH(0x0500, 0, false) { @Override public void preExecuteCheck(int paramCount, boolean returnValueExpected, MachineState state, short rawFunctionCode) throws ExecutionException { - state.api.platformSpecificPreExecuteCheck(rawFunctionCode, paramCount, returnValueExpected); + state.getAPI().platformSpecificPreExecuteCheck(rawFunctionCode, paramCount, returnValueExpected); } @Override protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException { - state.api.platformSpecificPostCheckExecute(rawFunctionCode, functionData, state); + state.getAPI().platformSpecificPostCheckExecute(rawFunctionCode, functionData, state); } }; @@ -943,7 +945,7 @@ public enum FunctionCode { if (functionData.paramCount == 2 && functionData.value2 == null) throw new IllegalFunctionCodeException("Passed value2 is null but function has paramCount of (" + this.paramCount + ")"); - state.logger.debug("Function \"" + this.name() + "\""); + state.getLogger().debug("Function \"" + this.name() + "\""); postCheckExecute(functionData, state, rawFunctionCode); } diff --git a/Java/src/org/ciyam/at/MachineState.java b/Java/src/org/ciyam/at/MachineState.java index 1175d42..011e1f4 100644 --- a/Java/src/org/ciyam/at/MachineState.java +++ b/Java/src/org/ciyam/at/MachineState.java @@ -4,12 +4,21 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.util.Arrays; +import java.util.HashMap; +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 + /** Size of one OpCode - typically 1 byte (byte) */ + public static final int OPCODE_SIZE = 1; + + /** Size of one FunctionCode - typically 2 bytes (short) */ + public static final int FUNCTIONCODE_SIZE = 2; + /** Size of value stored in data segment - typically 8 bytes (long) */ public static final int VALUE_SIZE = 8; @@ -19,162 +28,336 @@ public class MachineState { /** Maximum value for an address in the code segment */ public static final int MAX_CODE_ADDRESS = 0x1fffffff; - /** Bytes per code page */ - public static final int CODE_PAGE_SIZE = 1; + private static class VersionedConstants { + /** Bytes per code page */ + public final int CODE_PAGE_SIZE; + /** Bytes per data page */ + public final int DATA_PAGE_SIZE; + /** Bytes per call stack page */ + public final int CALL_STACK_PAGE_SIZE; + /** Bytes per user stack page */ + public final int USER_STACK_PAGE_SIZE; - /** Bytes per data page */ - public static final int DATA_PAGE_SIZE = VALUE_SIZE; - - /** Bytes per call stack page */ - public static final int CALL_STACK_PAGE_SIZE = ADDRESS_SIZE; - - /** Bytes per user stack page */ - public static final int USER_STACK_PAGE_SIZE = VALUE_SIZE; - - /** Program Counter: offset into code to point of current execution */ - public int programCounter; - - /** Initial program counter value to use on next block after current block's execution has stopped. 0 by default */ - public int onStopAddress; - - /** Program counter value to use if an error occurs during execution. If null upon error, refund all funds to creator and finish */ - public Integer onErrorAddress; - - /** Execution for current block has stopped. Continue at current program counter on next/specific block */ - public boolean isSleeping; - - /** Block height required to wake from sleeping, or null if not in use */ - public Integer sleepUntilHeight; - - /** Execution for current block has stopped. Restart at onStopAddress on next block */ - public boolean isStopped; - - /** Execution stopped due to lack of funds for processing. Restart at onStopAddress if frozenBalance increases */ - public boolean isFrozen; - - /** Balance at which there were not enough funds, or null if not in use */ - public Long frozenBalance; - - /** Execution permanently stopped */ - public boolean isFinished; - - /** Execution permanently stopped due to fatal error */ - public boolean hadFatalError; - - // 256-bit pseudo-registers - public long a1; - public long a2; - public long a3; - public long a4; - - public long b1; - public long b2; - public long b3; - public long b4; - - public int currentBlockHeight; - - /** Number of opcodes processed this execution */ - public int steps; - - public API api; - LoggerInterface logger; - - public short version; - public short reserved; - public short numCodePages; - public short numDataPages; - public short numCallStackPages; - public short numUserStackPages; - - public byte[] headerBytes; - - public ByteBuffer codeByteBuffer; - public ByteBuffer dataByteBuffer; - public ByteBuffer callStackByteBuffer; - public ByteBuffer userStackByteBuffer; - - private class Flags { - private int flags; - - public Flags() { - flags = 0; - } - - public Flags(int value) { - this.flags = value; - } - - public void push(boolean flag) { - flags <<= 1; - flags |= flag ? 1 : 0; - } - - public boolean pop() { - boolean result = (flags & 1) != 0; - flags >>>= 1; - return result; - } - - public int intValue() { - return flags; + public VersionedConstants(int codePageSize, int dataPageSize, int callStackPageSize, int userStackPageSize) { + CODE_PAGE_SIZE = codePageSize; + DATA_PAGE_SIZE = dataPageSize; + CALL_STACK_PAGE_SIZE = callStackPageSize; + USER_STACK_PAGE_SIZE = userStackPageSize; } } + /** Map of constants (e.g. CODE_PAGE_SIZE) by AT version */ + 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)); + } + + // Set during construction + public final short version; + public final short reserved; + public final short numCodePages; + public final short numDataPages; + public final short numCallStackPages; + public final short numUserStackPages; + + private final byte[] headerBytes; + + /** Constants set in effect */ + private final VersionedConstants constants; + + /** Program Counter: offset into code to point of current execution */ + private int programCounter; + + /** Initial program counter value to use on next block after current block's execution has stopped. 0 by default */ + private int onStopAddress; + + /** Program counter value to use if an error occurs during execution. If null upon error, refund all funds to creator and finish */ + private Integer onErrorAddress; + + /** Execution for current block has stopped. Continue at current program counter on next/specific block */ + private boolean isSleeping; + + /** Block height required to wake from sleeping, or null if not in use */ + private Integer sleepUntilHeight; + + /** Execution for current block has stopped. Restart at onStopAddress on next block */ + private boolean isStopped; + + /** Execution stopped due to lack of funds for processing. Restart at onStopAddress if frozenBalance increases */ + private boolean isFrozen; + + /** Balance at which there were not enough funds, or null if not in use */ + private Long frozenBalance; + + /** Execution permanently stopped */ + private boolean isFinished; + + /** Execution permanently stopped due to fatal error */ + private boolean hadFatalError; + + // 256-bit pseudo-registers + // NOTE: These are package-scope to allow easy access/operations in FunctionCodes. + // Outside classes (e.g. unit tests) can use getters + /* package */ long a1; + /* package */ long a2; + /* package */ long a3; + /* package */ long a4; + + /* package */ long b1; + /* package */ long b2; + /* package */ long b3; + /* package */ long b4; + + private int currentBlockHeight; + + /** Number of opcodes processed this execution */ + private int steps; + + private API api; + private LoggerInterface logger; + + // NOTE: These are package-scope to allow easy access/operations in Opcode/FunctionCode. + /* package */ ByteBuffer codeByteBuffer; + /* package */ ByteBuffer dataByteBuffer; + /* package */ ByteBuffer callStackByteBuffer; + /* package */ ByteBuffer userStackByteBuffer; + + // Constructors + /** For internal use when recreating a machine state */ private MachineState(API api, LoggerInterface logger, byte[] headerBytes) { if (headerBytes.length != HEADER_LENGTH) throw new IllegalArgumentException("headerBytes length " + headerBytes.length + " incorrect, expected " + HEADER_LENGTH); this.headerBytes = headerBytes; - parseHeader(); - this.codeByteBuffer = ByteBuffer.allocate(this.numCodePages * CODE_PAGE_SIZE).order(ByteOrder.LITTLE_ENDIAN); + // Parsing header bytes + ByteBuffer byteBuffer = ByteBuffer.wrap(this.headerBytes); + byteBuffer.order(ByteOrder.LITTLE_ENDIAN); - this.dataByteBuffer = ByteBuffer.allocate(this.numDataPages * DATA_PAGE_SIZE).order(ByteOrder.LITTLE_ENDIAN); + this.version = byteBuffer.getShort(); + if (this.version < 1) + throw new IllegalArgumentException("Version must be >= 0"); - this.callStackByteBuffer = ByteBuffer.allocate(this.numCallStackPages * CALL_STACK_PAGE_SIZE).order(ByteOrder.LITTLE_ENDIAN); + this.constants = VERSIONED_CONSTANTS.get(this.version); + if (this.constants == null) + throw new IllegalArgumentException("Version " + this.version + " unsupported"); + + this.reserved = byteBuffer.getShort(); + + this.numCodePages = byteBuffer.getShort(); + if (this.numCodePages < 1) + throw new IllegalArgumentException("Number of code pages must be > 0"); + + this.numDataPages = byteBuffer.getShort(); + if (this.numDataPages < 1) + throw new IllegalArgumentException("Number of data pages must be > 0"); + + this.numCallStackPages = byteBuffer.getShort(); + if (this.numCallStackPages < 0) + throw new IllegalArgumentException("Number of call stack pages must be >= 0"); + + this.numUserStackPages = byteBuffer.getShort(); + if (this.numUserStackPages < 0) + throw new IllegalArgumentException("Number of user stack pages must be >= 0"); + + // 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); + + // Set up stacks + this.callStackByteBuffer = ByteBuffer.allocate(this.numCallStackPages * this.constants.CALL_STACK_PAGE_SIZE).order(ByteOrder.LITTLE_ENDIAN); this.callStackByteBuffer.position(this.callStackByteBuffer.limit()); // Downward-growing stack, so start at the end - this.userStackByteBuffer = ByteBuffer.allocate(this.numUserStackPages * USER_STACK_PAGE_SIZE).order(ByteOrder.LITTLE_ENDIAN); + this.userStackByteBuffer = ByteBuffer.allocate(this.numUserStackPages * this.constants.USER_STACK_PAGE_SIZE).order(ByteOrder.LITTLE_ENDIAN); this.userStackByteBuffer.position(this.userStackByteBuffer.limit()); // Downward-growing stack, so start at the end this.api = api; - this.currentBlockHeight = api.getCurrentBlockHeight(); + this.currentBlockHeight = 0; this.steps = 0; this.logger = logger; } + /** For creating a new machine state */ + public MachineState(byte[] creationBytes) { + this(null, null, Arrays.copyOfRange(creationBytes, 0, HEADER_LENGTH)); + + int expectedLength = HEADER_LENGTH + this.numCodePages * this.constants.CODE_PAGE_SIZE + this.numDataPages + this.constants.DATA_PAGE_SIZE; + if (creationBytes.length != expectedLength) + throw new IllegalArgumentException("Creation bytes length does not match header values"); + + System.arraycopy(creationBytes, HEADER_LENGTH, this.codeByteBuffer.array(), 0, this.numCodePages * this.constants.CODE_PAGE_SIZE); + + System.arraycopy(creationBytes, HEADER_LENGTH + this.numCodePages * this.constants.CODE_PAGE_SIZE, this.dataByteBuffer.array(), 0, + this.numDataPages + this.constants.DATA_PAGE_SIZE); + + commonFinalConstruction(); + } + /** For creating a new machine state */ public MachineState(API api, LoggerInterface logger, byte[] headerBytes, byte[] codeBytes, byte[] dataBytes) { this(api, logger, headerBytes); - // XXX: Why don't we simply ByteBuffer.wrap(codeBytes) as they're read-only? - // This would do away with the need to specify numCodePages, save space and provide automatic end-of-code detection during execution thanks to - // ByteBuffer's BufferUnderflowException - - if (codeBytes.length > this.numCodePages * CODE_PAGE_SIZE) + if (codeBytes.length > this.numCodePages * this.constants.CODE_PAGE_SIZE) throw new IllegalArgumentException("Number of code pages too small to hold code bytes"); - if (dataBytes.length > this.numDataPages * DATA_PAGE_SIZE) + if (dataBytes.length > this.numDataPages * this.constants.DATA_PAGE_SIZE) throw new IllegalArgumentException("Number of data pages too small to hold data bytes"); System.arraycopy(codeBytes, 0, this.codeByteBuffer.array(), 0, codeBytes.length); System.arraycopy(dataBytes, 0, this.dataByteBuffer.array(), 0, dataBytes.length); + commonFinalConstruction(); + } + + private void commonFinalConstruction() { this.programCounter = 0; this.onStopAddress = 0; this.onErrorAddress = null; this.isSleeping = false; this.sleepUntilHeight = null; this.isStopped = false; - this.isFinished = false; - this.hadFatalError = false; this.isFrozen = false; this.frozenBalance = null; + this.isFinished = false; + this.hadFatalError = false; } + // Getters / setters + + // NOTE: Many setters have package-scope (i.e. org.ciyam.at only) to allow changes + // during execution but not by outside classes. + + public int getProgramCounter() { + return this.programCounter; + } + + public int getOnStopAddress() { + return this.onStopAddress; + } + + /* package */ void setOnStopAddress(int address) { + this.onStopAddress = address; + } + + public Integer getOnErrorAddress() { + return this.onErrorAddress; + } + + /* package */ void setOnErrorAddress(Integer address) { + this.onErrorAddress = address; + } + + public boolean getIsSleeping() { + return this.isSleeping; + } + + /* package */ void setIsSleeping(boolean isSleeping) { + this.isSleeping = isSleeping; + } + + public Integer getSleepUntilHeight() { + return this.sleepUntilHeight; + } + + /* package */ void setSleepUntilHeight(Integer address) { + this.sleepUntilHeight = address; + } + + public boolean getIsStopped() { + return this.isStopped; + } + + /* package */ void setIsStopped(boolean isStopped) { + this.isStopped = isStopped; + } + + public boolean getIsFrozen() { + return this.isFrozen; + } + + /* package */ void setIsFrozen(boolean isFrozen) { + this.isFrozen = isFrozen; + } + + public Long getFrozenBalance() { + return this.frozenBalance; + } + + /* package */ void setFrozenBalance(Long frozenBalance) { + this.frozenBalance = frozenBalance; + } + + public boolean getIsFinished() { + return this.isFinished; + } + + /* package */ void setIsFinished(boolean isFinished) { + this.isFinished = isFinished; + } + + public boolean getHadFatalError() { + return this.hadFatalError; + } + + /* package */ void setHadFatalError(boolean hadFatalError) { + this.hadFatalError = hadFatalError; + } + + // No corresponding setters due to package-scope - see above + public long getA1() { + return this.a1; + } + + public long getA2() { + return this.a2; + } + + public long getA3() { + return this.a3; + } + + public long getA4() { + return this.a4; + } + + public long getB1() { + return this.b1; + } + + public long getB2() { + return this.b2; + } + + public long getB3() { + return this.b3; + } + + public long getB4() { + return this.b4; + } + // End of package-scope pseudo-registers + + public int getCurrentBlockHeight() { + return this.currentBlockHeight; + } + + public int getSteps() { + return this.steps; + } + + public API getAPI() { + return this.api; + } + + public LoggerInterface getLogger() { + return this.logger; + } + + // Serialization + /** For serializing a machine state */ public byte[] toBytes() { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); @@ -192,13 +375,13 @@ public class MachineState { // Call stack length (32bit unsigned int) int callStackLength = this.callStackByteBuffer.limit() - this.callStackByteBuffer.position(); bytes.write(toByteArray(callStackLength)); - // Call stack + // Call stack (only the bytes actually in use) bytes.write(this.callStackByteBuffer.array(), this.callStackByteBuffer.position(), callStackLength); // User stack length (32bit unsigned int) int userStackLength = this.userStackByteBuffer.limit() - this.userStackByteBuffer.position(); bytes.write(toByteArray(userStackLength)); - // User stack + // User stack (only the bytes actually in use) bytes.write(this.userStackByteBuffer.array(), this.userStackByteBuffer.position(), userStackLength); // Actual state @@ -333,6 +516,34 @@ public class MachineState { return state; } + /** Class for pushing/popping boolean flags onto/from an int */ + private class Flags { + private int flags; + + public Flags() { + flags = 0; + } + + public Flags(int value) { + this.flags = value; + } + + public void push(boolean flag) { + flags <<= 1; + flags |= flag ? 1 : 0; + } + + public boolean pop() { + boolean result = (flags & 1) != 0; + flags >>>= 1; + return result; + } + + public int intValue() { + return flags; + } + } + /** Convert int to little-endian byte array */ private byte[] toByteArray(int value) { return new byte[] { (byte) (value), (byte) (value >> 8), (byte) (value >> 16), (byte) (value >> 24) }; @@ -344,32 +555,7 @@ public class MachineState { (byte) (value >> 48), (byte) (value >> 56) }; } - private void parseHeader() { - ByteBuffer byteBuffer = ByteBuffer.wrap(this.headerBytes); - byteBuffer.order(ByteOrder.LITTLE_ENDIAN); - - this.version = byteBuffer.getShort(); - if (this.version < 1) - throw new IllegalArgumentException("Version must be >= 0"); - - this.reserved = byteBuffer.getShort(); - - this.numCodePages = byteBuffer.getShort(); - if (this.numCodePages < 1) - throw new IllegalArgumentException("Number of code pages must be > 0"); - - this.numDataPages = byteBuffer.getShort(); - if (this.numDataPages < 1) - throw new IllegalArgumentException("Number of data pages must be > 0"); - - this.numCallStackPages = byteBuffer.getShort(); - if (this.numCallStackPages < 0) - throw new IllegalArgumentException("Number of call stack pages must be >= 0"); - - this.numUserStackPages = byteBuffer.getShort(); - if (this.numUserStackPages < 0) - throw new IllegalArgumentException("Number of user stack pages must be >= 0"); - } + // Actual execution public void execute() { // Set byte buffer position using program counter @@ -382,6 +568,7 @@ public class MachineState { this.isFrozen = false; this.frozenBalance = null; this.steps = 0; + this.currentBlockHeight = api.getCurrentBlockHeight(); while (!this.isSleeping && !this.isStopped && !this.isFinished && !this.isFrozen) { byte rawOpCode = codeByteBuffer.get(); @@ -393,7 +580,12 @@ public class MachineState { this.logger.debug("[PC: " + String.format("%04x", this.programCounter) + "] " + nextOpCode.name()); - nextOpCode.execute(codeByteBuffer, dataByteBuffer, userStackByteBuffer, callStackByteBuffer, this); + // TODO: Request cost from API, apply cost to balance, etc. + + // At this point, programCounter is BEFORE opcode (and args). + nextOpCode.execute(this); + + // Synchronize programCounter with codeByteBuffer in case of JMPs, branches, etc. this.programCounter = codeByteBuffer.position(); } catch (ExecutionException e) { this.logger.debug("Error at PC " + String.format("%04x", this.programCounter) + ": " + e.getMessage()); @@ -401,6 +593,8 @@ public class MachineState { if (this.onErrorAddress == null) { this.isFinished = true; this.hadFatalError = true; + + // Ask API to refund remaining funds back to AT's creator this.api.onFatalError(this, e); break; } @@ -418,7 +612,7 @@ public class MachineState { } } - // public String disassemble(List dataLabels, Map codeLabels) { + /** Return disassembly of code bytes */ public String disassemble() throws ExecutionException { String output = ""; diff --git a/Java/src/org/ciyam/at/OpCode.java b/Java/src/org/ciyam/at/OpCode.java index 8e69040..3fbfcbb 100644 --- a/Java/src/org/ciyam/at/OpCode.java +++ b/Java/src/org/ciyam/at/OpCode.java @@ -2,7 +2,9 @@ package org.ciyam.at; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -26,7 +28,7 @@ import java.util.stream.Collectors; * $($addr1 + $addr2) means "fetch from address fetched from addr1 plus offset fetched from addr2", i.e. indirect indexed * * @see OpCode#valueOf(int) - * @see OpCode#execute(ByteBuffer, ByteBuffer, ByteBuffer, ByteBuffer, MachineState) + * @see OpCode#executeWithParams(MachineState, Object...) */ public enum OpCode { @@ -37,8 +39,7 @@ public enum OpCode { */ NOP(0x7f) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) { + public void executeWithParams(MachineState state, Object... args) { // Do nothing } }, @@ -49,11 +50,11 @@ public enum OpCode { */ SET_VAL(0x01, OpCodeParam.DEST_ADDR, OpCodeParam.VALUE) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - int address = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - long value = Utils.getCodeValue(codeByteBuffer); - dataByteBuffer.putLong(address, value); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + int address = (int) args[0]; + long value = (long) args[1]; + + state.dataByteBuffer.putLong(address, value); } }, /** @@ -63,12 +64,12 @@ public enum OpCode { */ SET_DAT(0x02, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - int address1 = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - int address2 = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - long value = dataByteBuffer.getLong(address2); - dataByteBuffer.putLong(address1, value); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + int address1 = (int) args[0]; + int address2 = (int) args[1]; + + long value = state.dataByteBuffer.getLong(address2); + state.dataByteBuffer.putLong(address1, value); } }, /** @@ -78,10 +79,10 @@ public enum OpCode { */ CLR_DAT(0x03, OpCodeParam.DEST_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - int address = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - dataByteBuffer.putLong(address, 0L); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + int address = (int) args[0]; + + state.dataByteBuffer.putLong(address, 0L); } }, /** @@ -91,11 +92,11 @@ public enum OpCode { */ INC_DAT(0x04, OpCodeParam.DEST_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - int address = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - long value = dataByteBuffer.getLong(address); - dataByteBuffer.putLong(address, value + 1); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + int address = (int) args[0]; + + long value = state.dataByteBuffer.getLong(address); + state.dataByteBuffer.putLong(address, value + 1); } }, /** @@ -105,11 +106,11 @@ public enum OpCode { */ DEC_DAT(0x05, OpCodeParam.DEST_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - int address = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - long value = dataByteBuffer.getLong(address); - dataByteBuffer.putLong(address, value - 1); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + int address = (int) args[0]; + + long value = state.dataByteBuffer.getLong(address); + state.dataByteBuffer.putLong(address, value - 1); } }, /** @@ -119,9 +120,8 @@ public enum OpCode { */ ADD_DAT(0x06, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - executeDataOperation(codeByteBuffer, dataByteBuffer, (a, b) -> a + b); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + executeDataOperation(state, (a, b) -> a + b, args); } }, /** @@ -131,9 +131,8 @@ public enum OpCode { */ SUB_DAT(0x07, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - executeDataOperation(codeByteBuffer, dataByteBuffer, (a, b) -> a - b); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + executeDataOperation(state, (a, b) -> a - b, args); } }, /** @@ -143,9 +142,8 @@ public enum OpCode { */ MUL_DAT(0x08, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - executeDataOperation(codeByteBuffer, dataByteBuffer, (a, b) -> a * b); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + executeDataOperation(state, (a, b) -> a * b, args); } }, /** @@ -156,10 +154,9 @@ public enum OpCode { */ DIV_DAT(0x09, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { try { - executeDataOperation(codeByteBuffer, dataByteBuffer, (a, b) -> a / b); + executeDataOperation(state, (a, b) -> a / b, args); } catch (ArithmeticException e) { throw new IllegalOperationException("Divide by zero", e); } @@ -172,9 +169,8 @@ public enum OpCode { */ BOR_DAT(0x0a, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - executeDataOperation(codeByteBuffer, dataByteBuffer, (a, b) -> a | b); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + executeDataOperation(state, (a, b) -> a | b, args); } }, /** @@ -184,9 +180,8 @@ public enum OpCode { */ AND_DAT(0x0b, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - executeDataOperation(codeByteBuffer, dataByteBuffer, (a, b) -> a & b); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + executeDataOperation(state, (a, b) -> a & b, args); } }, /** @@ -196,9 +191,8 @@ public enum OpCode { */ XOR_DAT(0x0c, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - executeDataOperation(codeByteBuffer, dataByteBuffer, (a, b) -> a ^ b); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + executeDataOperation(state, (a, b) -> a ^ b, args); } }, /** @@ -208,11 +202,11 @@ public enum OpCode { */ NOT_DAT(0x0d, OpCodeParam.DEST_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - int address = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - long value = dataByteBuffer.getLong(address); - dataByteBuffer.putLong(address, ~value); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + int address = (int) args[0]; + + long value = state.dataByteBuffer.getLong(address); + state.dataByteBuffer.putLong(address, ~value); } }, /** @@ -222,18 +216,17 @@ public enum OpCode { */ SET_IND(0x0e, OpCodeParam.DEST_ADDR, OpCodeParam.INDIRECT_SRC_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - int address1 = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - int address2 = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + int address1 = (int) args[0]; + int address2 = (int) args[1]; - long address3 = dataByteBuffer.getLong(address2) * MachineState.VALUE_SIZE; + long address3 = state.dataByteBuffer.getLong(address2) * MachineState.VALUE_SIZE; - if (address3 < 0 || address3 + MachineState.VALUE_SIZE >= dataByteBuffer.limit()) + if (address3 < 0 || address3 + MachineState.VALUE_SIZE >= state.dataByteBuffer.limit()) throw new InvalidAddressException("Data address out of bounds"); - long value = dataByteBuffer.getLong((int) address3); - dataByteBuffer.putLong(address1, value); + long value = state.dataByteBuffer.getLong((int) address3); + state.dataByteBuffer.putLong(address1, value); } }, /** @@ -243,22 +236,21 @@ public enum OpCode { */ SET_IDX(0x0f, OpCodeParam.DEST_ADDR, OpCodeParam.INDIRECT_SRC_ADDR_WITH_INDEX, OpCodeParam.INDEX) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - int address1 = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - int address2 = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - int address3 = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + int address1 = (int) args[0]; + int address2 = (int) args[1]; + int address3 = (int) args[2]; - long baseAddress = dataByteBuffer.getLong(address2) * MachineState.VALUE_SIZE; - long offset = dataByteBuffer.getLong(address3) * MachineState.VALUE_SIZE; + long baseAddress = state.dataByteBuffer.getLong(address2) * MachineState.VALUE_SIZE; + long offset = state.dataByteBuffer.getLong(address3) * MachineState.VALUE_SIZE; long newAddress = baseAddress + offset; - if (newAddress < 0 || newAddress + MachineState.VALUE_SIZE >= dataByteBuffer.limit()) + if (newAddress < 0 || newAddress + MachineState.VALUE_SIZE >= state.dataByteBuffer.limit()) throw new InvalidAddressException("Data address out of bounds"); - long value = dataByteBuffer.getLong((int) newAddress); - dataByteBuffer.putLong(address1, value); + long value = state.dataByteBuffer.getLong((int) newAddress); + state.dataByteBuffer.putLong(address1, value); } }, /** @@ -269,16 +261,16 @@ public enum OpCode { */ PSH_DAT(0x10, OpCodeParam.SRC_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - int address = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - long value = dataByteBuffer.getLong(address); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + int address = (int) args[0]; + + long value = state.dataByteBuffer.getLong(address); try { // Simulate backwards-walking stack - int newPosition = userStackByteBuffer.position() - MachineState.VALUE_SIZE; - userStackByteBuffer.putLong(newPosition, value); - userStackByteBuffer.position(newPosition); + int newPosition = state.userStackByteBuffer.position() - MachineState.VALUE_SIZE; + state.userStackByteBuffer.putLong(newPosition, value); + state.userStackByteBuffer.position(newPosition); } catch (IndexOutOfBoundsException | IllegalArgumentException e) { throw new StackBoundsException("No room on user stack to push data", e); } @@ -292,18 +284,17 @@ public enum OpCode { */ POP_DAT(0x11, OpCodeParam.DEST_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - int address = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + int address = (int) args[0]; try { - long value = userStackByteBuffer.getLong(); + long value = state.userStackByteBuffer.getLong(); // Clear old stack entry - userStackByteBuffer.putLong(userStackByteBuffer.position() - MachineState.VALUE_SIZE, 0L); + state.userStackByteBuffer.putLong(state.userStackByteBuffer.position() - MachineState.VALUE_SIZE, 0L); // Put popped value into data address - dataByteBuffer.putLong(address, value); + state.dataByteBuffer.putLong(address, value); } catch (BufferUnderflowException e) { throw new StackBoundsException("Empty user stack from which to pop data", e); } @@ -317,20 +308,19 @@ public enum OpCode { */ JMP_SUB(0x12, OpCodeParam.CODE_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - int address = Utils.getCodeAddress(codeByteBuffer); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + int address = (int) args[0]; try { // Simulate backwards-walking stack - int newPosition = callStackByteBuffer.position() - MachineState.ADDRESS_SIZE; - callStackByteBuffer.putInt(newPosition, codeByteBuffer.position()); - callStackByteBuffer.position(newPosition); + int newPosition = state.callStackByteBuffer.position() - MachineState.ADDRESS_SIZE; + state.callStackByteBuffer.putInt(newPosition, state.codeByteBuffer.position()); + state.callStackByteBuffer.position(newPosition); } catch (IndexOutOfBoundsException | IllegalArgumentException e) { throw new StackBoundsException("No room on call stack to call subroutine", e); } - codeByteBuffer.position(address); + state.codeByteBuffer.position(address); } }, /** @@ -341,15 +331,14 @@ public enum OpCode { */ RET_SUB(0x13) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { try { - int returnAddress = callStackByteBuffer.getInt(); + int returnAddress = state.callStackByteBuffer.getInt(); // Clear old stack entry - callStackByteBuffer.putInt(callStackByteBuffer.position() - MachineState.ADDRESS_SIZE, 0); + state.callStackByteBuffer.putInt(state.callStackByteBuffer.position() - MachineState.ADDRESS_SIZE, 0); - codeByteBuffer.position(returnAddress); + state.codeByteBuffer.position(returnAddress); } catch (BufferUnderflowException e) { throw new StackBoundsException("Empty call stack missing return address from subroutine", e); } @@ -362,18 +351,17 @@ public enum OpCode { */ IND_DAT(0x14, OpCodeParam.INDIRECT_DEST_ADDR, OpCodeParam.SRC_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - int address1 = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - int address2 = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + int address1 = (int) args[0]; + int address2 = (int) args[1]; - long address3 = dataByteBuffer.getLong(address1) * MachineState.VALUE_SIZE; + long address3 = state.dataByteBuffer.getLong(address1) * MachineState.VALUE_SIZE; - if (address3 < 0 || address3 + MachineState.VALUE_SIZE >= dataByteBuffer.limit()) + if (address3 < 0 || address3 + MachineState.VALUE_SIZE >= state.dataByteBuffer.limit()) throw new InvalidAddressException("Data address out of bounds"); - long value = dataByteBuffer.getLong(address2); - dataByteBuffer.putLong((int) address3, value); + long value = state.dataByteBuffer.getLong(address2); + state.dataByteBuffer.putLong((int) address3, value); } }, /** @@ -383,22 +371,21 @@ public enum OpCode { */ IDX_DAT(0x15, OpCodeParam.INDIRECT_DEST_ADDR_WITH_INDEX, OpCodeParam.INDEX, OpCodeParam.SRC_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - int address1 = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - int address2 = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - int address3 = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + int address1 = (int) args[0]; + int address2 = (int) args[1]; + int address3 = (int) args[2]; - long baseAddress = dataByteBuffer.getLong(address1) * MachineState.VALUE_SIZE; - long offset = dataByteBuffer.getLong(address2) * MachineState.VALUE_SIZE; + long baseAddress = state.dataByteBuffer.getLong(address1) * MachineState.VALUE_SIZE; + long offset = state.dataByteBuffer.getLong(address2) * MachineState.VALUE_SIZE; long newAddress = baseAddress + offset; - if (newAddress < 0 || newAddress + MachineState.VALUE_SIZE >= dataByteBuffer.limit()) + if (newAddress < 0 || newAddress + MachineState.VALUE_SIZE >= state.dataByteBuffer.limit()) throw new InvalidAddressException("Data address out of bounds"); - long value = dataByteBuffer.getLong(address3); - dataByteBuffer.putLong((int) newAddress, value); + long value = state.dataByteBuffer.getLong(address3); + state.dataByteBuffer.putLong((int) newAddress, value); } }, /** @@ -408,10 +395,9 @@ public enum OpCode { */ MOD_DAT(0x16, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { try { - executeDataOperation(codeByteBuffer, dataByteBuffer, (a, b) -> a % b); + executeDataOperation(state, (a, b) -> a % b, args); } catch (ArithmeticException e) { throw new IllegalOperationException("Divide by zero", e); } @@ -426,10 +412,9 @@ public enum OpCode { private static final long MAX_SHIFT = MachineState.VALUE_SIZE * 8; @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { // If 2nd arg is more than value size (in bits) then return 0 to simulate all bits being shifted out of existence - executeDataOperation(codeByteBuffer, dataByteBuffer, (a, b) -> b >= MAX_SHIFT ? 0 : a << b); + executeDataOperation(state, (a, b) -> b >= MAX_SHIFT ? 0 : a << b, args); } }, /** @@ -442,10 +427,9 @@ public enum OpCode { private static final long MAX_SHIFT = MachineState.VALUE_SIZE * 8; @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { // If 2nd arg is more than value size (in bits) then return 0 to simulate all bits being shifted out of existence - executeDataOperation(codeByteBuffer, dataByteBuffer, (a, b) -> b >= MAX_SHIFT ? 0 : a >>> b); + executeDataOperation(state, (a, b) -> b >= MAX_SHIFT ? 0 : a >>> b, args); } }, /** @@ -455,11 +439,10 @@ public enum OpCode { */ JMP_ADR(0x1a, OpCodeParam.CODE_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - int address = Utils.getCodeAddress(codeByteBuffer); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + int address = (int) args[0]; - codeByteBuffer.position(address); + state.codeByteBuffer.position(address); } }, /** @@ -470,22 +453,19 @@ public enum OpCode { */ BZR_DAT(0x1b, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - int opCodePosition = codeByteBuffer.position() - 1; // i.e. before this OpCode + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + int address = (int) args[0]; + byte offset = (byte) args[1]; - int address = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - byte offset = Utils.getCodeOffset(codeByteBuffer); + int branchTarget = state.getProgramCounter() + offset; - int branchTarget = opCodePosition + offset; - - if (branchTarget < 0 || branchTarget >= codeByteBuffer.limit()) + if (branchTarget < 0 || branchTarget >= state.codeByteBuffer.limit()) throw new InvalidAddressException("branch target out of bounds"); - long value = dataByteBuffer.getLong(address); + long value = state.dataByteBuffer.getLong(address); if (value == 0) - codeByteBuffer.position(branchTarget); + state.codeByteBuffer.position(branchTarget); } }, /** @@ -496,22 +476,19 @@ public enum OpCode { */ BNZ_DAT(0x1e, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - int opCodePosition = codeByteBuffer.position() - 1; // i.e. before this OpCode + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + int address = (int) args[0]; + byte offset = (byte) args[1]; - int address = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - byte offset = Utils.getCodeOffset(codeByteBuffer); + int branchTarget = state.getProgramCounter() + offset; - int branchTarget = opCodePosition + offset; - - if (branchTarget < 0 || branchTarget >= codeByteBuffer.limit()) + if (branchTarget < 0 || branchTarget >= state.codeByteBuffer.limit()) throw new InvalidAddressException("branch target out of bounds"); - long value = dataByteBuffer.getLong(address); + long value = state.dataByteBuffer.getLong(address); if (value != 0) - codeByteBuffer.position(branchTarget); + state.codeByteBuffer.position(branchTarget); } }, /** @@ -522,9 +499,8 @@ public enum OpCode { */ BGT_DAT(0x1f, OpCodeParam.SRC_ADDR, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - executeBranchConditional(codeByteBuffer, dataByteBuffer, state, (a, b) -> a > b); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + executeBranchConditional(state, (a, b) -> a > b, args); } }, /** @@ -535,9 +511,8 @@ public enum OpCode { */ BLT_DAT(0x20, OpCodeParam.SRC_ADDR, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - executeBranchConditional(codeByteBuffer, dataByteBuffer, state, (a, b) -> a < b); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + executeBranchConditional(state, (a, b) -> a < b, args); } }, /** @@ -548,9 +523,8 @@ public enum OpCode { */ BGE_DAT(0x21, OpCodeParam.SRC_ADDR, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - executeBranchConditional(codeByteBuffer, dataByteBuffer, state, (a, b) -> a >= b); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + executeBranchConditional(state, (a, b) -> a >= b, args); } }, /** @@ -561,9 +535,8 @@ public enum OpCode { */ BLE_DAT(0x22, OpCodeParam.SRC_ADDR, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - executeBranchConditional(codeByteBuffer, dataByteBuffer, state, (a, b) -> a <= b); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + executeBranchConditional(state, (a, b) -> a <= b, args); } }, /** @@ -574,9 +547,8 @@ public enum OpCode { */ BEQ_DAT(0x23, OpCodeParam.SRC_ADDR, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - executeBranchConditional(codeByteBuffer, dataByteBuffer, state, (a, b) -> a == b); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + executeBranchConditional(state, (a, b) -> a == b, args); } }, /** @@ -587,9 +559,8 @@ public enum OpCode { */ BNE_DAT(0x24, OpCodeParam.SRC_ADDR, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - executeBranchConditional(codeByteBuffer, dataByteBuffer, state, (a, b) -> a != b); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + executeBranchConditional(state, (a, b) -> a != b, args); } }, /** @@ -600,13 +571,13 @@ public enum OpCode { */ SLP_DAT(0x25, OpCodeParam.BLOCK_HEIGHT) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - int address = Utils.getCodeAddress(codeByteBuffer); - long value = codeByteBuffer.getLong(address); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + int address = (int) args[0]; - state.sleepUntilHeight = (int) value; - state.isSleeping = true; + long value = state.codeByteBuffer.getLong(address); + + state.setSleepUntilHeight((int) value); + state.setIsSleeping(true); } }, /** @@ -616,13 +587,13 @@ public enum OpCode { */ FIZ_DAT(0x26, OpCodeParam.SRC_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - int address = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - long value = dataByteBuffer.getLong(address); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + int address = (int) args[0]; + + long value = state.dataByteBuffer.getLong(address); if (value == 0) - state.isFinished = true; + state.setIsFinished(true); } }, /** @@ -632,15 +603,14 @@ public enum OpCode { */ STZ_DAT(0x27, OpCodeParam.SRC_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - int address = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - long value = dataByteBuffer.getLong(address); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + int address = (int) args[0]; + + long value = state.dataByteBuffer.getLong(address); if (value == 0) { - state.programCounter = state.onStopAddress; - codeByteBuffer.position(state.onStopAddress); - state.isStopped = true; + state.codeByteBuffer.position(state.getOnStopAddress()); + state.setIsStopped(true); } } }, @@ -651,9 +621,8 @@ public enum OpCode { */ FIN_IMD(0x28) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - state.isFinished = true; + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + state.setIsFinished(true); } }, /** @@ -663,9 +632,8 @@ public enum OpCode { */ STP_IMD(0x29) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) { - state.isStopped = true; + public void executeWithParams(MachineState state, Object... args) { + state.setIsStopped(true); } }, /** @@ -675,10 +643,9 @@ public enum OpCode { */ SLP_IMD(0x2a) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) { - state.sleepUntilHeight = state.currentBlockHeight + 1; - state.isSleeping = true; + public void executeWithParams(MachineState state, Object... args) { + state.setSleepUntilHeight(state.getCurrentBlockHeight() + 1); + state.setIsSleeping(true); } }, /** @@ -688,11 +655,10 @@ public enum OpCode { */ ERR_ADR(0x2b, OpCodeParam.CODE_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - int address = Utils.getCodeAddress(codeByteBuffer); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + int address = (int) args[0]; - state.onErrorAddress = address; + state.setOnErrorAddress(address); } }, /** @@ -703,9 +669,8 @@ public enum OpCode { */ SET_PCS(0x30) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) { - state.onStopAddress = codeByteBuffer.position(); + public void executeWithParams(MachineState state, Object... args) { + state.setOnStopAddress(state.codeByteBuffer.position()); } }, /** @@ -715,9 +680,9 @@ public enum OpCode { */ EXT_FUN(0x32, OpCodeParam.FUNC) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - short rawFunctionCode = codeByteBuffer.getShort(); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + short rawFunctionCode = (short) args[0]; + FunctionCode functionCode = FunctionCode.valueOf(rawFunctionCode); if (functionCode == null) @@ -727,7 +692,7 @@ public enum OpCode { FunctionData functionData = new FunctionData(false); - executeFunction(codeByteBuffer, functionCode, functionData, state, rawFunctionCode); + functionCode.execute(functionData, state, rawFunctionCode); } }, /** @@ -737,9 +702,10 @@ public enum OpCode { */ EXT_FUN_DAT(0x33, OpCodeParam.FUNC, OpCodeParam.SRC_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - short rawFunctionCode = codeByteBuffer.getShort(); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + short rawFunctionCode = (short) args[0]; + int address = (int) args[1]; + FunctionCode functionCode = FunctionCode.valueOf(rawFunctionCode); if (functionCode == null) @@ -747,12 +713,11 @@ public enum OpCode { functionCode.preExecuteCheck(1, false, state, rawFunctionCode); - int address = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - long value = dataByteBuffer.getLong(address); + long value = state.dataByteBuffer.getLong(address); FunctionData functionData = new FunctionData(value, false); - executeFunction(codeByteBuffer, functionCode, functionData, state, rawFunctionCode); + functionCode.execute(functionData, state, rawFunctionCode); } }, /** @@ -762,9 +727,11 @@ public enum OpCode { */ EXT_FUN_DAT_2(0x34, OpCodeParam.FUNC, OpCodeParam.SRC_ADDR, OpCodeParam.SRC_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - short rawFunctionCode = codeByteBuffer.getShort(); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + short rawFunctionCode = (short) args[0]; + int address1 = (int) args[1]; + int address2 = (int) args[2]; + FunctionCode functionCode = FunctionCode.valueOf(rawFunctionCode); if (functionCode == null) @@ -772,15 +739,12 @@ public enum OpCode { functionCode.preExecuteCheck(2, false, state, rawFunctionCode); - int address1 = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - int address2 = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - - long value1 = dataByteBuffer.getLong(address1); - long value2 = dataByteBuffer.getLong(address2); + long value1 = state.dataByteBuffer.getLong(address1); + long value2 = state.dataByteBuffer.getLong(address2); FunctionData functionData = new FunctionData(value1, value2, false); - executeFunction(codeByteBuffer, functionCode, functionData, state, rawFunctionCode); + functionCode.execute(functionData, state, rawFunctionCode); } }, /** @@ -790,9 +754,10 @@ public enum OpCode { */ EXT_FUN_RET(0x35, OpCodeParam.FUNC, OpCodeParam.DEST_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - short rawFunctionCode = codeByteBuffer.getShort(); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + short rawFunctionCode = (short) args[0]; + int address = (int) args[1]; + FunctionCode functionCode = FunctionCode.valueOf(rawFunctionCode); if (functionCode == null) @@ -800,16 +765,14 @@ public enum OpCode { functionCode.preExecuteCheck(0, true, state, rawFunctionCode); - int address = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - FunctionData functionData = new FunctionData(true); - executeFunction(codeByteBuffer, functionCode, functionData, state, rawFunctionCode); + functionCode.execute(functionData, state, rawFunctionCode); if (functionData.returnValue == null) throw new ExecutionException("Function failed to return a value as expected of EXT_FUN_RET"); - dataByteBuffer.putLong(address, functionData.returnValue); + state.dataByteBuffer.putLong(address, functionData.returnValue); } }, /** @@ -819,9 +782,11 @@ public enum OpCode { */ EXT_FUN_RET_DAT(0x36, OpCodeParam.FUNC, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - short rawFunctionCode = codeByteBuffer.getShort(); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + short rawFunctionCode = (short) args[0]; + int address1 = (int) args[1]; + int address2 = (int) args[2]; + FunctionCode functionCode = FunctionCode.valueOf(rawFunctionCode); if (functionCode == null) @@ -829,19 +794,16 @@ public enum OpCode { functionCode.preExecuteCheck(1, true, state, rawFunctionCode); - int address1 = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - int address2 = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - - long value = dataByteBuffer.getLong(address2); + long value = state.dataByteBuffer.getLong(address2); FunctionData functionData = new FunctionData(value, true); - executeFunction(codeByteBuffer, functionCode, functionData, state, rawFunctionCode); + functionCode.execute(functionData, state, rawFunctionCode); if (functionData.returnValue == null) throw new ExecutionException("Function failed to return a value as expected of EXT_FUN_RET_DAT"); - dataByteBuffer.putLong(address1, functionData.returnValue); + state.dataByteBuffer.putLong(address1, functionData.returnValue); } }, /** @@ -851,9 +813,12 @@ public enum OpCode { */ EXT_FUN_RET_DAT_2(0x37, OpCodeParam.FUNC, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR, OpCodeParam.SRC_ADDR) { @Override - public void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException { - short rawFunctionCode = codeByteBuffer.getShort(); + public void executeWithParams(MachineState state, Object... args) throws ExecutionException { + short rawFunctionCode = (short) args[0]; + int address1 = (int) args[1]; + int address2 = (int) args[2]; + int address3 = (int) args[3]; + FunctionCode functionCode = FunctionCode.valueOf(rawFunctionCode); if (functionCode == null) @@ -862,21 +827,17 @@ public enum OpCode { functionCode.preExecuteCheck(2, true, state, rawFunctionCode); - int address1 = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - int address2 = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - int address3 = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - - long value1 = dataByteBuffer.getLong(address2); - long value2 = dataByteBuffer.getLong(address3); + long value1 = state.dataByteBuffer.getLong(address2); + long value2 = state.dataByteBuffer.getLong(address3); FunctionData functionData = new FunctionData(value1, value2, true); - executeFunction(codeByteBuffer, functionCode, functionData, state, rawFunctionCode); + functionCode.execute(functionData, state, rawFunctionCode); if (functionData.returnValue == null) throw new ExecutionException("Function failed to return a value as expected of EXT_FUN_RET_DAT_2"); - dataByteBuffer.putLong(address1, functionData.returnValue); + state.dataByteBuffer.putLong(address1, functionData.returnValue); } }; @@ -896,26 +857,32 @@ public enum OpCode { } /** - * Execute OpCode + * Execute OpCode with args fetched from code bytes *

- * Assumes codeByteBuffer.position() is already placed immediately after opcode. + * Assumes codeByteBuffer.position() is already placed immediately after opcode and params.
+ * state.getProgramCounter() is available to return position immediately before opcode and params. *

- * Updates codeByteBuffer.position() as arguments are fetched, so caller should update state.programCounter using - * codeByteBuffer.position() on return. + * OpCode execution can modify codeByteBuffer.position() in cases like jumps, branches, etc. *

* Can also modify userStackByteBuffer and various fields of state. *

* Throws a subclass of ExecutionException on error, e.g. InvalidAddressException. * - * @param codeByteBuffer - * @param dataByteBuffer - * @param userStackByteBuffer - * @param callStackByteBuffer * @param state + * @param args + * * @throws ExecutionException */ - public abstract void execute(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, ByteBuffer userStackByteBuffer, ByteBuffer callStackByteBuffer, - MachineState state) throws ExecutionException; + public abstract void executeWithParams(MachineState state, Object... args) throws ExecutionException; + + public void execute(MachineState state) throws ExecutionException { + List args = new ArrayList(); + + for (OpCodeParam param : this.params) + args.add(param.fetch(state.codeByteBuffer, state.dataByteBuffer)); + + this.executeWithParams(state, args.toArray()); + } /** * Returns string representing disassembled OpCode and parameters @@ -930,9 +897,8 @@ public enum OpCode { int postOpcodeProgramCounter = codeByteBuffer.position(); - for (OpCodeParam param : this.params) { + for (OpCodeParam param : this.params) output += " " + param.disassemble(codeByteBuffer, dataByteBuffer, postOpcodeProgramCounter); - } return output; } @@ -946,16 +912,16 @@ public enum OpCode { * - typically a lambda operating on two long params, e.g. (a, b) -> a + b * @throws ExecutionException */ - private static void executeDataOperation(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, TwoValueOperator operator) throws ExecutionException { - int address1 = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - int address2 = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); + private static void executeDataOperation(MachineState state, TwoValueOperator operator, Object... args) throws ExecutionException { + int address1 = (int) args[0]; + int address2 = (int) args[1]; - long value1 = dataByteBuffer.getLong(address1); - long value2 = dataByteBuffer.getLong(address2); + long value1 = state.dataByteBuffer.getLong(address1); + long value2 = state.dataByteBuffer.getLong(address2); long newValue = operator.apply(value1, value2); - dataByteBuffer.putLong(address1, newValue); + state.dataByteBuffer.putLong(address1, newValue); } /** @@ -968,47 +934,18 @@ public enum OpCode { * - typically a lambda comparing two long params, e.g. (a, b) -> a == b * @throws ExecutionException */ - private static void executeBranchConditional(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer, MachineState state, TwoValueComparator comparator) - throws ExecutionException { - int opCodePosition = codeByteBuffer.position() - 1; // i.e. before this OpCode + private static void executeBranchConditional(MachineState state, TwoValueComparator comparator, Object... args) throws ExecutionException { + int address1 = (int) args[0]; + int address2 = (int) args[1]; + byte offset = (byte) args[2]; - int address1 = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - int address2 = Utils.getDataAddress(codeByteBuffer, dataByteBuffer); - byte offset = Utils.getCodeOffset(codeByteBuffer); + int branchTarget = state.getProgramCounter() + offset; - int branchTarget = opCodePosition + offset; - - long value1 = dataByteBuffer.getLong(address1); - long value2 = dataByteBuffer.getLong(address2); + long value1 = state.dataByteBuffer.getLong(address1); + long value2 = state.dataByteBuffer.getLong(address2); if (comparator.compare(value1, value2)) - codeByteBuffer.position(branchTarget); - } - - /** - * Common code for executing a function. - *

- * Updates programCounter to codeByteBuffer's position before calling function.
- * Adjusts codeByteBuffer position to programCounter after calling function. - *

- * This is needed for functions that might use/alter the programCounter during their execution,
- * for example {@link FunctionCode#GENERATE_RANDOM_USING_TX_IN_A} - * - * @see FunctionCode#GENERATE_RANDOM_USING_TX_IN_A - * - * @param codeByteBuffer - * @param functionCode - * @param functionData - * @param state - * @throws ExecutionException - */ - private static void executeFunction(ByteBuffer codeByteBuffer, FunctionCode functionCode, FunctionData functionData, MachineState state, - short rawFunctionCode) throws ExecutionException { - state.programCounter = codeByteBuffer.position(); - - functionCode.execute(functionData, state, rawFunctionCode); - - codeByteBuffer.position(state.programCounter); + state.codeByteBuffer.position(branchTarget); } } diff --git a/Java/tests/BranchingOpCodeTests.java b/Java/tests/BranchingOpCodeTests.java index 1f03606..87ab5e2 100644 --- a/Java/tests/BranchingOpCodeTests.java +++ b/Java/tests/BranchingOpCodeTests.java @@ -1,87 +1,12 @@ -import static common.TestUtils.hexToBytes; import static org.junit.Assert.*; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.security.Security; - -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.ciyam.at.API; import org.ciyam.at.ExecutionException; -import org.ciyam.at.MachineState; import org.ciyam.at.OpCode; -import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; -import common.TestAPI; -import common.TestLogger; +import common.ExecutableTest; -public class BranchingOpCodeTests { - - public TestLogger logger; - public API api; - public MachineState state; - public ByteBuffer codeByteBuffer; - - @BeforeClass - public static void beforeClass() { - Security.insertProviderAt(new BouncyCastleProvider(), 0); - } - - @Before - public void beforeTest() { - logger = new TestLogger(); - api = new TestAPI(); - codeByteBuffer = ByteBuffer.allocate(512).order(ByteOrder.LITTLE_ENDIAN); - } - - @After - public void afterTest() { - codeByteBuffer = null; - api = null; - logger = null; - } - - private void execute() { - System.out.println("Starting execution:"); - System.out.println("Current block height: " + state.currentBlockHeight); - - state.execute(); - - System.out.println("After execution:"); - System.out.println("Steps: " + state.steps); - System.out.println("Program Counter: " + String.format("%04x", state.programCounter)); - System.out.println("Stop Address: " + String.format("%04x", state.onStopAddress)); - System.out.println("Error Address: " + (state.onErrorAddress == null ? "not set" : String.format("%04x", state.onErrorAddress))); - if (state.isSleeping) - System.out.println("Sleeping until current block height (" + state.currentBlockHeight + ") reaches " + state.sleepUntilHeight); - else - System.out.println("Sleeping: " + state.isSleeping); - System.out.println("Stopped: " + state.isStopped); - System.out.println("Finished: " + state.isFinished); - if (state.hadFatalError) - System.out.println("Finished due to fatal error!"); - System.out.println("Frozen: " + state.isFrozen); - } - - private void simulate() { - // version 0003, reserved 0000, code 0200 * 1, data 0020 * 8, call stack 0010 * 4, user stack 0010 * 8 - byte[] headerBytes = hexToBytes("0300" + "0000" + "0002" + "2000" + "1000" + "1000"); - byte[] codeBytes = codeByteBuffer.array(); - byte[] dataBytes = new byte[0]; - - state = new MachineState(api, logger, headerBytes, codeBytes, dataBytes); - - do { - execute(); - - // Bump block height - state.currentBlockHeight++; - } while (!state.isFinished); - - } +public class BranchingOpCodeTests extends ExecutableTest { @Test public void testBZR_DATtrue() throws ExecutionException { @@ -98,11 +23,11 @@ public class BranchingOpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(1).putLong(2L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 2L, state.dataByteBuffer.getLong(1 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 2L, getData(1)); } @Test @@ -120,11 +45,11 @@ public class BranchingOpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(1).putLong(2L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 1L, state.dataByteBuffer.getLong(1 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 1L, getData(1)); } @Test @@ -142,11 +67,11 @@ public class BranchingOpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(1).putLong(2L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 2L, state.dataByteBuffer.getLong(1 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 2L, getData(1)); } @Test @@ -164,11 +89,11 @@ public class BranchingOpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(1).putLong(2L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 1L, state.dataByteBuffer.getLong(1 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 1L, getData(1)); } @Test @@ -187,11 +112,11 @@ public class BranchingOpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(2L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 2L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 2L, getData(2)); } @Test @@ -210,11 +135,11 @@ public class BranchingOpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(2L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 1L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 1L, getData(2)); } @Test @@ -233,11 +158,11 @@ public class BranchingOpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(2L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 2L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 2L, getData(2)); } @Test @@ -256,11 +181,11 @@ public class BranchingOpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(2L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 1L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 1L, getData(2)); } @Test @@ -279,11 +204,11 @@ public class BranchingOpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(2L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 2L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 2L, getData(2)); } @Test @@ -302,11 +227,11 @@ public class BranchingOpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(2L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 2L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 2L, getData(2)); } @Test @@ -325,11 +250,11 @@ public class BranchingOpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(2L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 1L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 1L, getData(2)); } @Test @@ -348,11 +273,11 @@ public class BranchingOpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(2L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 2L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 2L, getData(2)); } @Test @@ -371,11 +296,11 @@ public class BranchingOpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(2L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 2L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 2L, getData(2)); } @Test @@ -394,11 +319,11 @@ public class BranchingOpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(2L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 1L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 1L, getData(2)); } @Test @@ -417,11 +342,11 @@ public class BranchingOpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(2L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 2L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 2L, getData(2)); } @Test @@ -440,11 +365,11 @@ public class BranchingOpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(2L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 1L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 1L, getData(2)); } @Test @@ -463,11 +388,11 @@ public class BranchingOpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(2L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 2L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 2L, getData(2)); } @Test @@ -486,11 +411,11 @@ public class BranchingOpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(2L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 1L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 1L, getData(2)); } } diff --git a/Java/tests/CallStackOpCodeTests.java b/Java/tests/CallStackOpCodeTests.java index d4afa8c..c010070 100644 --- a/Java/tests/CallStackOpCodeTests.java +++ b/Java/tests/CallStackOpCodeTests.java @@ -1,87 +1,14 @@ -import static common.TestUtils.hexToBytes; +import static common.TestUtils.*; import static org.junit.Assert.*; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.security.Security; - -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.ciyam.at.API; import org.ciyam.at.ExecutionException; import org.ciyam.at.MachineState; import org.ciyam.at.OpCode; -import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; -import common.TestAPI; -import common.TestLogger; +import common.ExecutableTest; -public class CallStackOpCodeTests { - - public TestLogger logger; - public API api; - public MachineState state; - public ByteBuffer codeByteBuffer; - - @BeforeClass - public static void beforeClass() { - Security.insertProviderAt(new BouncyCastleProvider(), 0); - } - - @Before - public void beforeTest() { - logger = new TestLogger(); - api = new TestAPI(); - codeByteBuffer = ByteBuffer.allocate(512).order(ByteOrder.LITTLE_ENDIAN); - } - - @After - public void afterTest() { - codeByteBuffer = null; - api = null; - logger = null; - } - - private void execute() { - System.out.println("Starting execution:"); - System.out.println("Current block height: " + state.currentBlockHeight); - - state.execute(); - - System.out.println("After execution:"); - System.out.println("Steps: " + state.steps); - System.out.println("Program Counter: " + String.format("%04x", state.programCounter)); - System.out.println("Stop Address: " + String.format("%04x", state.onStopAddress)); - System.out.println("Error Address: " + (state.onErrorAddress == null ? "not set" : String.format("%04x", state.onErrorAddress))); - if (state.isSleeping) - System.out.println("Sleeping until current block height (" + state.currentBlockHeight + ") reaches " + state.sleepUntilHeight); - else - System.out.println("Sleeping: " + state.isSleeping); - System.out.println("Stopped: " + state.isStopped); - System.out.println("Finished: " + state.isFinished); - if (state.hadFatalError) - System.out.println("Finished due to fatal error!"); - System.out.println("Frozen: " + state.isFrozen); - } - - private void simulate() { - // version 0003, reserved 0000, code 0200 * 1, data 0020 * 8, call stack 0010 * 4, user stack 0010 * 8 - byte[] headerBytes = hexToBytes("0300" + "0000" + "0002" + "2000" + "1000" + "1000"); - byte[] codeBytes = codeByteBuffer.array(); - byte[] dataBytes = new byte[0]; - - state = new MachineState(api, logger, headerBytes, codeBytes, dataBytes); - - do { - execute(); - - // Bump block height - state.currentBlockHeight++; - } while (!state.isFinished); - - } +public class CallStackOpCodeTests extends ExecutableTest { @Test public void testJMP_SUB() throws ExecutionException { @@ -96,17 +23,17 @@ public class CallStackOpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(0).putLong(4444L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); - int expectedCallStackPosition = (state.numCallStackPages - 1) * MachineState.CALL_STACK_PAGE_SIZE; - assertEquals("Call stack pointer incorrect", expectedCallStackPosition, state.callStackByteBuffer.position()); + int expectedCallStackPosition = (state.numCallStackPages - 1) * CALL_STACK_PAGE_SIZE; + assertEquals("Call stack pointer incorrect", expectedCallStackPosition, getCallStackPosition()); - assertEquals("Return address does not match", returnAddress, state.callStackByteBuffer.getInt(expectedCallStackPosition)); + assertEquals("Return address does not match", returnAddress, getCallStackEntry(expectedCallStackPosition)); - assertEquals("Data does not match", 4444L, state.dataByteBuffer.getLong(0 * MachineState.VALUE_SIZE)); + assertEquals("Data does not match", 4444L, getData(0)); } @Test @@ -130,19 +57,19 @@ public class CallStackOpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(1).putLong(5555L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); - int expectedCallStackPosition = (state.numCallStackPages - 1 - 1) * MachineState.CALL_STACK_PAGE_SIZE; - assertEquals("Call stack pointer incorrect", expectedCallStackPosition, state.callStackByteBuffer.position()); + int expectedCallStackPosition = (state.numCallStackPages - 1 - 1) * CALL_STACK_PAGE_SIZE; + assertEquals("Call stack pointer incorrect", expectedCallStackPosition, getCallStackPosition()); - assertEquals("Return address does not match", returnAddress2, state.callStackByteBuffer.getInt(expectedCallStackPosition)); - assertEquals("Return address does not match", returnAddress1, state.callStackByteBuffer.getInt(expectedCallStackPosition + MachineState.ADDRESS_SIZE)); + assertEquals("Return address does not match", returnAddress2, getCallStackEntry(expectedCallStackPosition)); + assertEquals("Return address does not match", returnAddress1, getCallStackEntry(expectedCallStackPosition + MachineState.ADDRESS_SIZE)); - assertEquals("Data does not match", 4444L, state.dataByteBuffer.getLong(0 * MachineState.VALUE_SIZE)); - assertEquals("Data does not match", 5555L, state.dataByteBuffer.getLong(1 * MachineState.VALUE_SIZE)); + assertEquals("Data does not match", 4444L, getData(0)); + assertEquals("Data does not match", 5555L, getData(1)); } @Test @@ -154,10 +81,10 @@ public class CallStackOpCodeTests { } codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } @Test @@ -174,18 +101,18 @@ public class CallStackOpCodeTests { codeByteBuffer.put(OpCode.RET_SUB.value); codeByteBuffer.put(OpCode.FIN_IMD.value); // not reached! - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); - int expectedCallStackPosition = (state.numCallStackPages - 1 + 1) * MachineState.CALL_STACK_PAGE_SIZE; - assertEquals("Call stack pointer incorrect", expectedCallStackPosition, state.callStackByteBuffer.position()); + int expectedCallStackPosition = (state.numCallStackPages - 1 + 1) * CALL_STACK_PAGE_SIZE; + assertEquals("Call stack pointer incorrect", expectedCallStackPosition, getCallStackPosition()); - assertEquals("Return address not cleared", 0L, state.callStackByteBuffer.getInt(expectedCallStackPosition - MachineState.ADDRESS_SIZE)); + assertEquals("Return address not cleared", 0L, getCallStackEntry(expectedCallStackPosition - MachineState.ADDRESS_SIZE)); - assertEquals("Data does not match", 4444L, state.dataByteBuffer.getLong(0 * MachineState.VALUE_SIZE)); - assertEquals("Data does not match", 7777L, state.dataByteBuffer.getLong(1 * MachineState.VALUE_SIZE)); + assertEquals("Data does not match", 4444L, getData(0)); + assertEquals("Data does not match", 7777L, getData(1)); } @Test @@ -211,20 +138,20 @@ public class CallStackOpCodeTests { codeByteBuffer.put(OpCode.RET_SUB.value); codeByteBuffer.put(OpCode.FIN_IMD.value); // not reached! - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); - int expectedCallStackPosition = (state.numCallStackPages - 1 - 1 + 1 + 1) * MachineState.CALL_STACK_PAGE_SIZE; - assertEquals("Call stack pointer incorrect", expectedCallStackPosition, state.callStackByteBuffer.position()); + int expectedCallStackPosition = (state.numCallStackPages - 1 - 1 + 1 + 1) * CALL_STACK_PAGE_SIZE; + assertEquals("Call stack pointer incorrect", expectedCallStackPosition, getCallStackPosition()); - assertEquals("Return address not cleared", 0L, state.callStackByteBuffer.getInt(expectedCallStackPosition - MachineState.ADDRESS_SIZE)); + assertEquals("Return address not cleared", 0L, getCallStackEntry(expectedCallStackPosition - MachineState.ADDRESS_SIZE)); - assertEquals("Data does not match", 4444L, state.dataByteBuffer.getLong(0 * MachineState.VALUE_SIZE)); - assertEquals("Data does not match", 7777L, state.dataByteBuffer.getLong(1 * MachineState.VALUE_SIZE)); - assertEquals("Data does not match", 2222L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); - assertEquals("Data does not match", 3333L, state.dataByteBuffer.getLong(3 * MachineState.VALUE_SIZE)); + assertEquals("Data does not match", 4444L, getData(0)); + assertEquals("Data does not match", 7777L, getData(1)); + assertEquals("Data does not match", 2222L, getData(2)); + assertEquals("Data does not match", 3333L, getData(3)); } @Test @@ -232,10 +159,10 @@ public class CallStackOpCodeTests { codeByteBuffer.put(OpCode.RET_SUB.value); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } @Test @@ -246,10 +173,10 @@ public class CallStackOpCodeTests { codeByteBuffer.put(OpCode.RET_SUB.value); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } } diff --git a/Java/tests/DataOpCodeTests.java b/Java/tests/DataOpCodeTests.java index 6bdf312..9382146 100644 --- a/Java/tests/DataOpCodeTests.java +++ b/Java/tests/DataOpCodeTests.java @@ -1,98 +1,23 @@ -import static common.TestUtils.hexToBytes; import static org.junit.Assert.*; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.security.Security; - -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.ciyam.at.API; import org.ciyam.at.ExecutionException; -import org.ciyam.at.MachineState; import org.ciyam.at.OpCode; -import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; -import common.TestAPI; -import common.TestLogger; +import common.ExecutableTest; -public class DataOpCodeTests { - - public TestLogger logger; - public API api; - public MachineState state; - public ByteBuffer codeByteBuffer; - - @BeforeClass - public static void beforeClass() { - Security.insertProviderAt(new BouncyCastleProvider(), 0); - } - - @Before - public void beforeTest() { - logger = new TestLogger(); - api = new TestAPI(); - codeByteBuffer = ByteBuffer.allocate(512).order(ByteOrder.LITTLE_ENDIAN); - } - - @After - public void afterTest() { - codeByteBuffer = null; - api = null; - logger = null; - } - - private void execute() { - System.out.println("Starting execution:"); - System.out.println("Current block height: " + state.currentBlockHeight); - - state.execute(); - - System.out.println("After execution:"); - System.out.println("Steps: " + state.steps); - System.out.println("Program Counter: " + String.format("%04x", state.programCounter)); - System.out.println("Stop Address: " + String.format("%04x", state.onStopAddress)); - System.out.println("Error Address: " + (state.onErrorAddress == null ? "not set" : String.format("%04x", state.onErrorAddress))); - if (state.isSleeping) - System.out.println("Sleeping until current block height (" + state.currentBlockHeight + ") reaches " + state.sleepUntilHeight); - else - System.out.println("Sleeping: " + state.isSleeping); - System.out.println("Stopped: " + state.isStopped); - System.out.println("Finished: " + state.isFinished); - if (state.hadFatalError) - System.out.println("Finished due to fatal error!"); - System.out.println("Frozen: " + state.isFrozen); - } - - private void simulate() { - // version 0003, reserved 0000, code 0200 * 1, data 0020 * 8, call stack 0010 * 4, user stack 0010 * 8 - byte[] headerBytes = hexToBytes("0300" + "0000" + "0002" + "2000" + "1000" + "1000"); - byte[] codeBytes = codeByteBuffer.array(); - byte[] dataBytes = new byte[0]; - - state = new MachineState(api, logger, headerBytes, codeBytes, dataBytes); - - do { - execute(); - - // Bump block height - state.currentBlockHeight++; - } while (!state.isFinished); - - } +public class DataOpCodeTests extends ExecutableTest { @Test public void testSET_VAL() throws ExecutionException { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(2222L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 2222L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 2222L, getData(2)); } @Test @@ -100,10 +25,10 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(9999).putLong(2222L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } @Test @@ -112,11 +37,11 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.SET_DAT.value).putInt(1).putInt(2); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 2222L, state.dataByteBuffer.getLong(1 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 2222L, getData(1)); } @Test @@ -124,10 +49,10 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.SET_DAT.value).putInt(9999).putInt(2); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } @Test @@ -135,10 +60,10 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.SET_DAT.value).putInt(1).putInt(9999); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } @Test @@ -147,15 +72,14 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.CLR_DAT.value).putInt(2); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); // Check data all zero - state.dataByteBuffer.position(0); - while (state.dataByteBuffer.hasRemaining()) - assertEquals((byte) 0, state.dataByteBuffer.get()); + for (int i = 0; i < 0x0020; ++i) + assertEquals(0L, getData(i)); } @Test @@ -163,10 +87,10 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.CLR_DAT.value).putInt(9999); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } @Test @@ -175,11 +99,11 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.INC_DAT.value).putInt(2); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 2222L + 1L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 2222L + 1L, getData(2)); } @Test @@ -187,10 +111,10 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.INC_DAT.value).putInt(9999); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } @Test @@ -199,11 +123,11 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.INC_DAT.value).putInt(2); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 0L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 0L, getData(2)); } @Test @@ -212,11 +136,11 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.DEC_DAT.value).putInt(2); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 2222L - 1L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 2222L - 1L, getData(2)); } @Test @@ -224,9 +148,9 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.DEC_DAT.value).putInt(9999); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + execute(true); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } @Test @@ -235,11 +159,11 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.DEC_DAT.value).putInt(2); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 0xffffffffffffffffL, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 0xffffffffffffffffL, getData(2)); } @Test @@ -249,11 +173,11 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.ADD_DAT.value).putInt(2).putInt(3); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 2222L + 3333L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 2222L + 3333L, getData(2)); } @Test @@ -261,10 +185,10 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.ADD_DAT.value).putInt(9999).putInt(3); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } @Test @@ -272,10 +196,10 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.ADD_DAT.value).putInt(2).putInt(9999); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } @Test @@ -285,11 +209,11 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.ADD_DAT.value).putInt(2).putInt(3); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 0x0000000000000098L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 0x0000000000000098L, getData(2)); } @Test @@ -299,11 +223,11 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.SUB_DAT.value).putInt(3).putInt(2); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 3333L - 2222L, state.dataByteBuffer.getLong(3 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 3333L - 2222L, getData(3)); } @Test @@ -313,11 +237,11 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.MUL_DAT.value).putInt(3).putInt(2); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", (3333L * 2222L), state.dataByteBuffer.getLong(3 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", (3333L * 2222L), getData(3)); } @Test @@ -329,11 +253,11 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.DIV_DAT.value).putInt(3).putInt(2); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", (3333L / 2222L), state.dataByteBuffer.getLong(3 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", (3333L / 2222L), getData(3)); } @Test @@ -354,11 +278,11 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(1).putLong(1L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Error flag not set", 1L, state.dataByteBuffer.getLong(1 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Error flag not set", 1L, getData(1)); } @Test @@ -368,11 +292,11 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.BOR_DAT.value).putInt(3).putInt(2); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", (3333L | 2222L), state.dataByteBuffer.getLong(3 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", (3333L | 2222L), getData(3)); } @Test @@ -382,11 +306,11 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.AND_DAT.value).putInt(3).putInt(2); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", (3333L & 2222L), state.dataByteBuffer.getLong(3 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", (3333L & 2222L), getData(3)); } @Test @@ -396,11 +320,11 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.XOR_DAT.value).putInt(3).putInt(2); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", (3333L ^ 2222L), state.dataByteBuffer.getLong(3 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", (3333L ^ 2222L), getData(3)); } @Test @@ -409,11 +333,11 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.NOT_DAT.value).putInt(2); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", ~2222L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", ~2222L, getData(2)); } @Test @@ -428,11 +352,11 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.SET_IND.value).putInt(6).putInt(0); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 3333L, state.dataByteBuffer.getLong(6 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 3333L, getData(6)); } @Test @@ -447,10 +371,10 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.SET_IND.value).putInt(6).putInt(9999); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } @Test @@ -465,10 +389,10 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.SET_IND.value).putInt(6).putInt(0); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } @Test @@ -484,11 +408,11 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.SET_IDX.value).putInt(0).putInt(6).putInt(7); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 4444L, state.dataByteBuffer.getLong(0 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 4444L, getData(0)); } @Test @@ -504,10 +428,10 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.SET_IDX.value).putInt(0).putInt(9999).putInt(7); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } @Test @@ -523,10 +447,10 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.SET_IDX.value).putInt(0).putInt(6).putInt(7); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } @Test @@ -542,10 +466,10 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.SET_IDX.value).putInt(0).putInt(6).putInt(7); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } @Test @@ -561,10 +485,10 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.SET_IDX.value).putInt(0).putInt(6).putInt(9999); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } @Test @@ -579,11 +503,11 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.IND_DAT.value).putInt(0).putInt(5); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 5555L, state.dataByteBuffer.getLong(3 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 5555L, getData(3)); } @Test @@ -598,10 +522,10 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.SET_IND.value).putInt(9999).putInt(5); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } @Test @@ -616,10 +540,10 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.SET_IND.value).putInt(0).putInt(5); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } @Test @@ -635,11 +559,11 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.IDX_DAT.value).putInt(6).putInt(7).putInt(5); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 5555L, state.dataByteBuffer.getLong(4 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 5555L, getData(4)); } @Test @@ -655,10 +579,10 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.IDX_DAT.value).putInt(9999).putInt(7).putInt(5); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } @Test @@ -674,10 +598,10 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.IDX_DAT.value).putInt(6).putInt(7).putInt(5); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } @Test @@ -693,10 +617,10 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.IDX_DAT.value).putInt(6).putInt(7).putInt(5); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } @Test @@ -712,10 +636,10 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.IDX_DAT.value).putInt(6).putInt(9999).putInt(5); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } @Test @@ -725,11 +649,11 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.MOD_DAT.value).putInt(2).putInt(3); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 2222L % 3333L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 2222L % 3333L, getData(2)); } @Test @@ -750,11 +674,11 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(1).putLong(1L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Error flag not set", 1L, state.dataByteBuffer.getLong(1 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Error flag not set", 1L, getData(1)); } @Test @@ -764,11 +688,11 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.SHL_DAT.value).putInt(2).putInt(3); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 2222L << 3, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 2222L << 3, getData(2)); } @Test @@ -778,11 +702,11 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.SHL_DAT.value).putInt(2).putInt(3); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 0L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 0L, getData(2)); } @Test @@ -792,11 +716,11 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.SHR_DAT.value).putInt(2).putInt(3); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 2222L >> 3, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 2222L >> 3, getData(2)); } @Test @@ -806,11 +730,11 @@ public class DataOpCodeTests { codeByteBuffer.put(OpCode.SHR_DAT.value).putInt(2).putInt(3); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 0L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 0L, getData(2)); } } diff --git a/Java/tests/DisassemblyTests.java b/Java/tests/DisassemblyTests.java index 0812ec2..01b0fc3 100644 --- a/Java/tests/DisassemblyTests.java +++ b/Java/tests/DisassemblyTests.java @@ -44,28 +44,6 @@ public class DisassemblyTests { logger = null; } - private void execute() { - System.out.println("Starting execution:"); - System.out.println("Current block height: " + state.currentBlockHeight); - - state.execute(); - - System.out.println("After execution:"); - System.out.println("Steps: " + state.steps); - System.out.println("Program Counter: " + String.format("%04x", state.programCounter)); - System.out.println("Stop Address: " + String.format("%04x", state.onStopAddress)); - System.out.println("Error Address: " + (state.onErrorAddress == null ? "not set" : String.format("%04x", state.onErrorAddress))); - if (state.isSleeping) - System.out.println("Sleeping until current block height (" + state.currentBlockHeight + ") reaches " + state.sleepUntilHeight); - else - System.out.println("Sleeping: " + state.isSleeping); - System.out.println("Stopped: " + state.isStopped); - System.out.println("Finished: " + state.isFinished); - if (state.hadFatalError) - System.out.println("Finished due to fatal error!"); - System.out.println("Frozen: " + state.isFrozen); - } - @Test public void testMD160disassembly() throws ExecutionException { // MD160 of ffffffffffffffffffffffffffffffffffffffffffffffff is 90e735014ea23aa89190121b229c06d58fc71e83 diff --git a/Java/tests/FunctionCodeTests.java b/Java/tests/FunctionCodeTests.java index e9fd8df..f1e3ee6 100644 --- a/Java/tests/FunctionCodeTests.java +++ b/Java/tests/FunctionCodeTests.java @@ -1,89 +1,15 @@ import static common.TestUtils.hexToBytes; import static org.junit.Assert.*; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.security.Security; - -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.ciyam.at.API; import org.ciyam.at.ExecutionException; import org.ciyam.at.FunctionCode; -import org.ciyam.at.MachineState; import org.ciyam.at.OpCode; import org.ciyam.at.Timestamp; -import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; -import common.TestAPI; -import common.TestLogger; +import common.ExecutableTest; -public class FunctionCodeTests { - - public TestLogger logger; - public API api; - public MachineState state; - public ByteBuffer codeByteBuffer; - - @BeforeClass - public static void beforeClass() { - Security.insertProviderAt(new BouncyCastleProvider(), 0); - } - - @Before - public void beforeTest() { - logger = new TestLogger(); - api = new TestAPI(); - codeByteBuffer = ByteBuffer.allocate(512).order(ByteOrder.LITTLE_ENDIAN); - } - - @After - public void afterTest() { - codeByteBuffer = null; - api = null; - logger = null; - } - - private void execute() { - System.out.println("Starting execution:"); - System.out.println("Current block height: " + state.currentBlockHeight); - - state.execute(); - - System.out.println("After execution:"); - System.out.println("Steps: " + state.steps); - System.out.println("Program Counter: " + String.format("%04x", state.programCounter)); - System.out.println("Stop Address: " + String.format("%04x", state.onStopAddress)); - System.out.println("Error Address: " + (state.onErrorAddress == null ? "not set" : String.format("%04x", state.onErrorAddress))); - if (state.isSleeping) - System.out.println("Sleeping until current block height (" + state.currentBlockHeight + ") reaches " + state.sleepUntilHeight); - else - System.out.println("Sleeping: " + state.isSleeping); - System.out.println("Stopped: " + state.isStopped); - System.out.println("Finished: " + state.isFinished); - if (state.hadFatalError) - System.out.println("Finished due to fatal error!"); - System.out.println("Frozen: " + state.isFrozen); - } - - private void simulate() { - // version 0003, reserved 0000, code 0200 * 1, data 0020 * 8, call stack 0010 * 4, user stack 0010 * 8 - byte[] headerBytes = hexToBytes("0300" + "0000" + "0002" + "2000" + "1000" + "1000"); - byte[] codeBytes = codeByteBuffer.array(); - byte[] dataBytes = new byte[0]; - - state = new MachineState(api, logger, headerBytes, codeBytes, dataBytes); - - do { - execute(); - - // Bump block height - state.currentBlockHeight++; - } while (!state.isFinished); - - } +public class FunctionCodeTests extends ExecutableTest { @Test public void testMD5() throws ExecutionException { @@ -109,11 +35,11 @@ public class FunctionCodeTests { codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("MD5 hashes do not match", 1L, state.dataByteBuffer.getLong(1 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("MD5 hashes do not match", 1L, getData(1)); } @Test @@ -134,11 +60,11 @@ public class FunctionCodeTests { codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("MD5 hashes do not match", 1L, state.dataByteBuffer.getLong(1 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("MD5 hashes do not match", 1L, getData(1)); } @Test @@ -165,11 +91,11 @@ public class FunctionCodeTests { codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("RIPEMD160 hashes do not match", 1L, state.dataByteBuffer.getLong(1 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("RIPEMD160 hashes do not match", 1L, getData(1)); } @Test @@ -192,11 +118,11 @@ public class FunctionCodeTests { codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertEquals("RIPEMD160 hashes do not match", 1L, state.dataByteBuffer.getLong(1 * MachineState.VALUE_SIZE)); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); + assertEquals("RIPEMD160 hashes do not match", 1L, getData(1)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); } @Test @@ -223,11 +149,11 @@ public class FunctionCodeTests { codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("RIPEMD160 hashes do not match", 1L, state.dataByteBuffer.getLong(1 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("RIPEMD160 hashes do not match", 1L, getData(1)); } @Test @@ -252,11 +178,11 @@ public class FunctionCodeTests { codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertEquals("RIPEMD160 hashes do not match", 1L, state.dataByteBuffer.getLong(1 * MachineState.VALUE_SIZE)); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); + assertEquals("RIPEMD160 hashes do not match", 1L, getData(1)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); } @Test @@ -266,11 +192,11 @@ public class FunctionCodeTests { codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GENERATE_RANDOM_USING_TX_IN_A.value).putInt(1); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(false); - assertNotEquals("Random wasn't generated", 0L, state.dataByteBuffer.getLong(1 * MachineState.VALUE_SIZE)); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); + assertNotEquals("Random wasn't generated", 0L, getData(1)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); } @Test @@ -278,10 +204,10 @@ public class FunctionCodeTests { codeByteBuffer.put(OpCode.EXT_FUN.value).putShort((short) 0xaaaa); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } @Test @@ -290,10 +216,10 @@ public class FunctionCodeTests { codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort((short) 0x0501).putInt(0); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); } @Test @@ -302,10 +228,10 @@ public class FunctionCodeTests { codeByteBuffer.put(OpCode.EXT_FUN_RET_DAT_2.value).putShort((short) 0x0501).putInt(0).putInt(0); // Wrong OPCODE for function codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } } diff --git a/Java/tests/MiscTests.java b/Java/tests/MiscTests.java index c0a0d87..48241ae 100644 --- a/Java/tests/MiscTests.java +++ b/Java/tests/MiscTests.java @@ -1,88 +1,13 @@ -import static common.TestUtils.hexToBytes; import static org.junit.Assert.*; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.security.Security; - -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.ciyam.at.API; import org.ciyam.at.ExecutionException; import org.ciyam.at.FunctionCode; -import org.ciyam.at.MachineState; import org.ciyam.at.OpCode; -import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; -import common.TestAPI; -import common.TestLogger; +import common.ExecutableTest; -public class MiscTests { - - public TestLogger logger; - public API api; - public MachineState state; - public ByteBuffer codeByteBuffer; - - @BeforeClass - public static void beforeClass() { - Security.insertProviderAt(new BouncyCastleProvider(), 0); - } - - @Before - public void beforeTest() { - logger = new TestLogger(); - api = new TestAPI(); - codeByteBuffer = ByteBuffer.allocate(512).order(ByteOrder.LITTLE_ENDIAN); - } - - @After - public void afterTest() { - codeByteBuffer = null; - api = null; - logger = null; - } - - private void execute() { - System.out.println("Starting execution:"); - System.out.println("Current block height: " + state.currentBlockHeight); - - state.execute(); - - System.out.println("After execution:"); - System.out.println("Steps: " + state.steps); - System.out.println("Program Counter: " + String.format("%04x", state.programCounter)); - System.out.println("Stop Address: " + String.format("%04x", state.onStopAddress)); - System.out.println("Error Address: " + (state.onErrorAddress == null ? "not set" : String.format("%04x", state.onErrorAddress))); - if (state.isSleeping) - System.out.println("Sleeping until current block height (" + state.currentBlockHeight + ") reaches " + state.sleepUntilHeight); - else - System.out.println("Sleeping: " + state.isSleeping); - System.out.println("Stopped: " + state.isStopped); - System.out.println("Finished: " + state.isFinished); - if (state.hadFatalError) - System.out.println("Finished due to fatal error!"); - System.out.println("Frozen: " + state.isFrozen); - } - - private void simulate() { - // version 0003, reserved 0000, code 0200 * 1, data 0020 * 8, call stack 0010 * 4, user stack 0010 * 8 - byte[] headerBytes = hexToBytes("0300" + "0000" + "0002" + "2000" + "1000" + "1000"); - byte[] codeBytes = codeByteBuffer.array(); - byte[] dataBytes = new byte[0]; - - state = new MachineState(api, logger, headerBytes, codeBytes, dataBytes); - - do { - execute(); - - // Bump block height - state.currentBlockHeight++; - } while (!state.isFinished); - - } +public class MiscTests extends ExecutableTest { @Test public void testSimpleCode() throws ExecutionException { @@ -91,21 +16,21 @@ public class MiscTests { codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.ECHO.value).putInt(0); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", testValue, state.dataByteBuffer.getLong(0 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", testValue, getData(0)); } @Test public void testInvalidOpCode() throws ExecutionException { codeByteBuffer.put((byte) 0xdd); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } } diff --git a/Java/tests/OpCodeTests.java b/Java/tests/OpCodeTests.java index 7ee530d..1327310 100644 --- a/Java/tests/OpCodeTests.java +++ b/Java/tests/OpCodeTests.java @@ -1,102 +1,27 @@ import static common.TestUtils.hexToBytes; import static org.junit.Assert.*; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.security.Security; - -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.ciyam.at.API; import org.ciyam.at.ExecutionException; -import org.ciyam.at.MachineState; import org.ciyam.at.OpCode; -import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; -import common.TestAPI; -import common.TestLogger; +import common.ExecutableTest; -public class OpCodeTests { - - public TestLogger logger; - public API api; - public MachineState state; - public ByteBuffer codeByteBuffer; - - @BeforeClass - public static void beforeClass() { - Security.insertProviderAt(new BouncyCastleProvider(), 0); - } - - @Before - public void beforeTest() { - logger = new TestLogger(); - api = new TestAPI(); - codeByteBuffer = ByteBuffer.allocate(512).order(ByteOrder.LITTLE_ENDIAN); - } - - @After - public void afterTest() { - codeByteBuffer = null; - api = null; - logger = null; - } - - private void execute() { - System.out.println("Starting execution:"); - System.out.println("Current block height: " + state.currentBlockHeight); - - state.execute(); - - System.out.println("After execution:"); - System.out.println("Steps: " + state.steps); - System.out.println("Program Counter: " + String.format("%04x", state.programCounter)); - System.out.println("Stop Address: " + String.format("%04x", state.onStopAddress)); - System.out.println("Error Address: " + (state.onErrorAddress == null ? "not set" : String.format("%04x", state.onErrorAddress))); - if (state.isSleeping) - System.out.println("Sleeping until current block height (" + state.currentBlockHeight + ") reaches " + state.sleepUntilHeight); - else - System.out.println("Sleeping: " + state.isSleeping); - System.out.println("Stopped: " + state.isStopped); - System.out.println("Finished: " + state.isFinished); - if (state.hadFatalError) - System.out.println("Finished due to fatal error!"); - System.out.println("Frozen: " + state.isFrozen); - } - - private void simulate() { - // version 0003, reserved 0000, code 0200 * 1, data 0020 * 8, call stack 0010 * 4, user stack 0010 * 8 - byte[] headerBytes = hexToBytes("0300" + "0000" + "0002" + "2000" + "1000" + "1000"); - byte[] codeBytes = codeByteBuffer.array(); - byte[] dataBytes = new byte[0]; - - state = new MachineState(api, logger, headerBytes, codeBytes, dataBytes); - - do { - execute(); - - // Bump block height - state.currentBlockHeight++; - } while (!state.isFinished && !state.isFrozen && !state.isSleeping && !state.isStopped); - - } +public class OpCodeTests extends ExecutableTest { @Test public void testNOP() throws ExecutionException { codeByteBuffer.put(OpCode.NOP.value); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); // Check data unchanged - state.dataByteBuffer.position(0); - while (state.dataByteBuffer.hasRemaining()) - assertEquals((byte) 0, state.dataByteBuffer.get()); + for (int i = 0; i < 0x0020; ++i) + assertEquals(0L, getData(i)); } @Test @@ -111,11 +36,11 @@ public class OpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(0).putLong(2L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Data does not match", 2L, state.dataByteBuffer.getLong(0 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Data does not match", 2L, getData(0)); } @Test @@ -126,12 +51,12 @@ public class OpCodeTests { codeByteBuffer.put(OpCode.SLP_DAT.value).putInt(0); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isSleeping); - assertFalse(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Sleep-until block height incorrect", blockHeight, state.dataByteBuffer.getLong(0 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsSleeping()); + assertFalse(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Sleep-until block height incorrect", blockHeight, getData(0)); } @Test @@ -140,11 +65,11 @@ public class OpCodeTests { codeByteBuffer.put(OpCode.FIZ_DAT.value).putInt(0); codeByteBuffer.put(OpCode.SLP_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.isSleeping); - assertFalse(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertFalse(state.getIsSleeping()); + assertFalse(state.getHadFatalError()); } @Test @@ -153,11 +78,11 @@ public class OpCodeTests { codeByteBuffer.put(OpCode.FIZ_DAT.value).putInt(0); codeByteBuffer.put(OpCode.SLP_IMD.value); - simulate(); + execute(true); - assertFalse(state.isFinished); - assertTrue(state.isSleeping); - assertFalse(state.hadFatalError); + assertFalse(state.getIsFinished()); + assertTrue(state.getIsSleeping()); + assertFalse(state.getHadFatalError()); } @Test @@ -168,12 +93,12 @@ public class OpCodeTests { codeByteBuffer.put(OpCode.STZ_DAT.value).putInt(0); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isStopped); - assertFalse(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Program counter incorrect", stopAddress, state.programCounter); + assertTrue(state.getIsStopped()); + assertFalse(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Program counter incorrect", stopAddress, state.getProgramCounter()); } @Test @@ -183,11 +108,11 @@ public class OpCodeTests { codeByteBuffer.put(OpCode.STZ_DAT.value).putInt(0); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertFalse(state.isStopped); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); + assertFalse(state.getIsStopped()); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); } @Test @@ -195,11 +120,11 @@ public class OpCodeTests { codeByteBuffer.put(OpCode.FIN_IMD.value); codeByteBuffer.put(OpCode.STP_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.isStopped); - assertFalse(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertFalse(state.getIsStopped()); + assertFalse(state.getHadFatalError()); } @Test @@ -210,12 +135,12 @@ public class OpCodeTests { codeByteBuffer.put(OpCode.STP_IMD.value); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isStopped); - assertFalse(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Program counter incorrect", stopAddress, state.programCounter); + assertTrue(state.getIsStopped()); + assertFalse(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Program counter incorrect", stopAddress, state.getProgramCounter()); } @Test @@ -224,12 +149,12 @@ public class OpCodeTests { int nextAddress = codeByteBuffer.position(); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isSleeping); - assertFalse(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Program counter incorrect", nextAddress, state.programCounter); + assertTrue(state.getIsSleeping()); + assertFalse(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Program counter incorrect", nextAddress, state.getProgramCounter()); } @Test @@ -250,11 +175,11 @@ public class OpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(1L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Error flag not set", 1L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Error flag not set", 1L, getData(2)); } @Test @@ -267,11 +192,11 @@ public class OpCodeTests { int expectedStopAddress = codeByteBuffer.position(); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertEquals(expectedStopAddress, state.onStopAddress); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); + assertEquals(expectedStopAddress, state.getOnStopAddress()); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); } @Test @@ -282,11 +207,11 @@ public class OpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(0).put(hexToBytes("0000000022222222")); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertEquals(expectedStopAddress, state.onStopAddress); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); + assertEquals(expectedStopAddress, state.getOnStopAddress()); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); } } diff --git a/Java/tests/SerializationTests.java b/Java/tests/SerializationTests.java index bd6e48b..7f08d6a 100644 --- a/Java/tests/SerializationTests.java +++ b/Java/tests/SerializationTests.java @@ -5,7 +5,6 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; -import org.ciyam.at.API; import org.ciyam.at.ExecutionException; import org.ciyam.at.FunctionCode; import org.ciyam.at.MachineState; @@ -20,7 +19,7 @@ import common.TestLogger; public class SerializationTests { public TestLogger logger; - public API api; + public TestAPI api; public MachineState state; public ByteBuffer codeByteBuffer; @@ -53,7 +52,7 @@ public class SerializationTests { state = MachineState.fromBytes(api, logger, savedState); // Pretend we're on next block - state.currentBlockHeight++; + api.bumpCurrentBlockHeight(); return executeAndCheck(state); } @@ -79,9 +78,9 @@ public class SerializationTests { simulate(); - assertEquals(0x0e, (int) state.onStopAddress); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); + assertEquals(0x0e, (int) state.getOnStopAddress()); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); } @Test @@ -98,9 +97,9 @@ public class SerializationTests { byte[] savedState = simulate(); - assertEquals(0x0e, (int) state.onStopAddress); - assertTrue(state.isStopped); - assertFalse(state.hadFatalError); + assertEquals(0x0e, (int) state.getOnStopAddress()); + assertTrue(state.getIsStopped()); + assertFalse(state.getHadFatalError()); savedState = continueSimulation(savedState); savedState = continueSimulation(savedState); diff --git a/Java/tests/TestACCT.java b/Java/tests/TestACCT.java index 37b8d4e..c974f63 100644 --- a/Java/tests/TestACCT.java +++ b/Java/tests/TestACCT.java @@ -65,9 +65,6 @@ public class TestACCT { private byte[] continueSimulation(byte[] savedState) { state = MachineState.fromBytes(api, logger, savedState); - // Pretend we're on next block - state.currentBlockHeight++; - return executeAndCheck(state); } @@ -208,8 +205,8 @@ public class TestACCT { byte[] savedState = simulate(); - while (!state.isFinished) { - ((ACCTAPI) state.api).generateNextBlock(secret); + while (!state.getIsFinished()) { + ((ACCTAPI) state.getAPI()).generateNextBlock(secret); savedState = continueSimulation(savedState); } diff --git a/Java/tests/UserStackOpCodeTests.java b/Java/tests/UserStackOpCodeTests.java index 6c701ed..38ef017 100644 --- a/Java/tests/UserStackOpCodeTests.java +++ b/Java/tests/UserStackOpCodeTests.java @@ -1,87 +1,13 @@ -import static common.TestUtils.hexToBytes; +import static common.TestUtils.*; import static org.junit.Assert.*; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.security.Security; - -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.ciyam.at.API; import org.ciyam.at.ExecutionException; -import org.ciyam.at.MachineState; import org.ciyam.at.OpCode; -import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; -import common.TestAPI; -import common.TestLogger; +import common.ExecutableTest; -public class UserStackOpCodeTests { - - public TestLogger logger; - public API api; - public MachineState state; - public ByteBuffer codeByteBuffer; - - @BeforeClass - public static void beforeClass() { - Security.insertProviderAt(new BouncyCastleProvider(), 0); - } - - @Before - public void beforeTest() { - logger = new TestLogger(); - api = new TestAPI(); - codeByteBuffer = ByteBuffer.allocate(512).order(ByteOrder.LITTLE_ENDIAN); - } - - @After - public void afterTest() { - codeByteBuffer = null; - api = null; - logger = null; - } - - private void execute() { - System.out.println("Starting execution:"); - System.out.println("Current block height: " + state.currentBlockHeight); - - state.execute(); - - System.out.println("After execution:"); - System.out.println("Steps: " + state.steps); - System.out.println("Program Counter: " + String.format("%04x", state.programCounter)); - System.out.println("Stop Address: " + String.format("%04x", state.onStopAddress)); - System.out.println("Error Address: " + (state.onErrorAddress == null ? "not set" : String.format("%04x", state.onErrorAddress))); - if (state.isSleeping) - System.out.println("Sleeping until current block height (" + state.currentBlockHeight + ") reaches " + state.sleepUntilHeight); - else - System.out.println("Sleeping: " + state.isSleeping); - System.out.println("Stopped: " + state.isStopped); - System.out.println("Finished: " + state.isFinished); - if (state.hadFatalError) - System.out.println("Finished due to fatal error!"); - System.out.println("Frozen: " + state.isFrozen); - } - - private void simulate() { - // version 0003, reserved 0000, code 0200 * 1, data 0020 * 8, call stack 0010 * 4, user stack 0010 * 8 - byte[] headerBytes = hexToBytes("0300" + "0000" + "0002" + "2000" + "1000" + "1000"); - byte[] codeBytes = codeByteBuffer.array(); - byte[] dataBytes = new byte[0]; - - state = new MachineState(api, logger, headerBytes, codeBytes, dataBytes); - - do { - execute(); - - // Bump block height - state.currentBlockHeight++; - } while (!state.isFinished); - - } +public class UserStackOpCodeTests extends ExecutableTest { @Test public void testPSH_DAT() throws ExecutionException { @@ -89,14 +15,14 @@ public class UserStackOpCodeTests { codeByteBuffer.put(OpCode.PSH_DAT.value).putInt(0); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); - int expectedUserStackPosition = (state.numUserStackPages - 1) * MachineState.USER_STACK_PAGE_SIZE; - assertEquals("User stack pointer incorrect", expectedUserStackPosition, state.userStackByteBuffer.position()); - assertEquals("Data does not match", 4444L, state.userStackByteBuffer.getLong(expectedUserStackPosition)); + int expectedUserStackPosition = (state.numUserStackPages - 1) * USER_STACK_PAGE_SIZE; + assertEquals("User stack pointer incorrect", expectedUserStackPosition, getUserStackPosition()); + assertEquals("Data does not match", 4444L, getUserStackEntry(expectedUserStackPosition)); } @Test @@ -107,14 +33,14 @@ public class UserStackOpCodeTests { codeByteBuffer.put(OpCode.PSH_DAT.value).putInt(1); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); - int expectedUserStackPosition = (state.numUserStackPages - 2) * MachineState.USER_STACK_PAGE_SIZE; - assertEquals("User stack pointer incorrect", expectedUserStackPosition, state.userStackByteBuffer.position()); - assertEquals("Data does not match", 3333L, state.userStackByteBuffer.getLong(expectedUserStackPosition)); + int expectedUserStackPosition = (state.numUserStackPages - 2) * USER_STACK_PAGE_SIZE; + assertEquals("User stack pointer incorrect", expectedUserStackPosition, getUserStackPosition()); + assertEquals("Data does not match", 3333L, getUserStackEntry(expectedUserStackPosition)); } @Test @@ -126,10 +52,10 @@ public class UserStackOpCodeTests { } codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } @Test @@ -150,11 +76,11 @@ public class UserStackOpCodeTests { codeByteBuffer.put(OpCode.SET_VAL.value).putInt(1).putLong(1L); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); - assertEquals("Error flag not set", 1L, state.dataByteBuffer.getLong(1 * MachineState.VALUE_SIZE)); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); + assertEquals("Error flag not set", 1L, getData(1)); } @Test @@ -164,15 +90,16 @@ public class UserStackOpCodeTests { codeByteBuffer.put(OpCode.POP_DAT.value).putInt(1); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); - int expectedUserStackPosition = (state.numUserStackPages - 1 + 1) * MachineState.USER_STACK_PAGE_SIZE; - assertEquals("User stack pointer incorrect", expectedUserStackPosition, state.userStackByteBuffer.position()); - assertEquals("Data does not match", 4444L, state.dataByteBuffer.getLong(1 * MachineState.VALUE_SIZE)); - assertEquals("Stack entry not cleared", 0L, state.userStackByteBuffer.getLong(expectedUserStackPosition - MachineState.VALUE_SIZE)); + int expectedUserStackPosition = (state.numUserStackPages - 1 + 1) * USER_STACK_PAGE_SIZE; + assertEquals("User stack pointer incorrect", expectedUserStackPosition, getUserStackPosition()); + assertEquals("Data does not match", 4444L, getData(1)); + // Following test is not applicable when using serialized state: + // assertEquals("Stack entry not cleared", 0L, getUserStackEntry(expectedUserStackPosition - MachineState.VALUE_SIZE)); } @Test @@ -185,15 +112,15 @@ public class UserStackOpCodeTests { codeByteBuffer.put(OpCode.POP_DAT.value).putInt(3); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertFalse(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertFalse(state.getHadFatalError()); - int expectedUserStackPosition = (state.numUserStackPages - 1 - 1 + 1 + 1) * MachineState.USER_STACK_PAGE_SIZE; - assertEquals("User stack pointer incorrect", expectedUserStackPosition, state.userStackByteBuffer.position()); - assertEquals("Data does not match", 3333L, state.dataByteBuffer.getLong(2 * MachineState.VALUE_SIZE)); - assertEquals("Data does not match", 4444L, state.dataByteBuffer.getLong(3 * MachineState.VALUE_SIZE)); + int expectedUserStackPosition = (state.numUserStackPages - 1 - 1 + 1 + 1) * USER_STACK_PAGE_SIZE; + assertEquals("User stack pointer incorrect", expectedUserStackPosition, getUserStackPosition()); + assertEquals("Data does not match", 3333L, getData(2)); + assertEquals("Data does not match", 4444L, getData(3)); } @Test @@ -201,10 +128,10 @@ public class UserStackOpCodeTests { codeByteBuffer.put(OpCode.POP_DAT.value).putInt(0); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } @Test @@ -215,10 +142,10 @@ public class UserStackOpCodeTests { codeByteBuffer.put(OpCode.POP_DAT.value).putInt(2); codeByteBuffer.put(OpCode.FIN_IMD.value); - simulate(); + execute(true); - assertTrue(state.isFinished); - assertTrue(state.hadFatalError); + assertTrue(state.getIsFinished()); + assertTrue(state.getHadFatalError()); } } diff --git a/Java/tests/common/ACCTAPI.java b/Java/tests/common/ACCTAPI.java index 3719c5e..4eef496 100644 --- a/Java/tests/common/ACCTAPI.java +++ b/Java/tests/common/ACCTAPI.java @@ -14,7 +14,7 @@ import org.ciyam.at.IllegalFunctionCodeException; import org.ciyam.at.MachineState; import org.ciyam.at.Timestamp; -public class ACCTAPI implements API { +public class ACCTAPI extends API { private class Account { public String address; @@ -130,6 +130,7 @@ public class ACCTAPI implements API { } /** Convert long to little-endian byte array */ + @SuppressWarnings("unused") private byte[] toByteArray(long value) { return new byte[] { (byte) (value), (byte) (value >> 8), (byte) (value >> 16), (byte) (value >> 24), (byte) (value >> 32), (byte) (value >> 40), (byte) (value >> 48), (byte) (value >> 56) }; @@ -161,10 +162,10 @@ public class ACCTAPI implements API { @Override public void putPreviousBlockHashInA(MachineState state) { - state.a1 = this.blockchain.size() - 1; - state.a2 = state.a1; - state.a3 = state.a1; - state.a4 = state.a1; + this.setA1(state, this.blockchain.size() - 1); + this.setA2(state, state.getA1()); + this.setA3(state, state.getA1()); + this.setA4(state, state.getA1()); } @Override @@ -191,10 +192,10 @@ public class ACCTAPI implements API { System.out.println("Found transaction at height " + blockHeight + " sequence " + transactionSequence); // Generate pseudo-hash of transaction - state.a1 = new Timestamp(blockHeight, transactionSequence).longValue(); - state.a2 = state.a1; - state.a3 = state.a1; - state.a4 = state.a1; + this.setA1(state, new Timestamp(blockHeight, transactionSequence).longValue()); + this.setA2(state, state.getA1()); + this.setA3(state, state.getA1()); + this.setA4(state, state.getA1()); return; } @@ -202,15 +203,15 @@ public class ACCTAPI implements API { } // Nothing found - state.a1 = 0L; - state.a2 = 0L; - state.a3 = 0L; - state.a4 = 0L; + this.setA1(state, 0L); + this.setA2(state, 0L); + this.setA3(state, 0L); + this.setA4(state, 0L); } @Override public long getTypeFromTransactionInA(MachineState state) { - Timestamp timestamp = new Timestamp(state.a1); + Timestamp timestamp = new Timestamp(state.getA1()); Block block = this.blockchain.get(timestamp.blockHeight - 1); Transaction transaction = block.transactions.get(timestamp.transactionSequence); return transaction.txType; @@ -218,7 +219,7 @@ public class ACCTAPI implements API { @Override public long getAmountFromTransactionInA(MachineState state) { - Timestamp timestamp = new Timestamp(state.a1); + Timestamp timestamp = new Timestamp(state.getA1()); Block block = this.blockchain.get(timestamp.blockHeight - 1); Transaction transaction = block.transactions.get(timestamp.transactionSequence); return transaction.amount; @@ -227,7 +228,7 @@ public class ACCTAPI implements API { @Override public long getTimestampFromTransactionInA(MachineState state) { // Transaction hash in A is actually just 4 copies of transaction's "timestamp" - Timestamp timestamp = new Timestamp(state.a1); + Timestamp timestamp = new Timestamp(state.getA1()); return timestamp.longValue(); } @@ -239,33 +240,33 @@ public class ACCTAPI implements API { @Override public void putMessageFromTransactionInAIntoB(MachineState state) { - Timestamp timestamp = new Timestamp(state.a1); + Timestamp timestamp = new Timestamp(state.getA1()); Block block = this.blockchain.get(timestamp.blockHeight - 1); Transaction transaction = block.transactions.get(timestamp.transactionSequence); - state.b1 = transaction.message[0]; - state.b2 = transaction.message[1]; - state.b3 = transaction.message[2]; - state.b4 = transaction.message[3]; + this.setB1(state, transaction.message[0]); + this.setB2(state, transaction.message[1]); + this.setB3(state, transaction.message[2]); + this.setB4(state, transaction.message[3]); } @Override public void putAddressFromTransactionInAIntoB(MachineState state) { - Timestamp timestamp = new Timestamp(state.a1); + Timestamp timestamp = new Timestamp(state.getA1()); Block block = this.blockchain.get(timestamp.blockHeight - 1); Transaction transaction = block.transactions.get(timestamp.transactionSequence); - state.b1 = transaction.creator.charAt(0); - state.b2 = state.b1; - state.b3 = state.b1; - state.b4 = state.b1; + this.setB1(state, transaction.creator.charAt(0)); + this.setB2(state, state.getB1()); + this.setB3(state, state.getB1()); + this.setB4(state, state.getB1()); } @Override public void putCreatorAddressIntoB(MachineState state) { // Dummy creator - state.b1 = "C".charAt(0); - state.b2 = state.b1; - state.b3 = state.b1; - state.b4 = state.b1; + this.setB1(state, "C".charAt(0)); + this.setB2(state, state.getB1()); + this.setB3(state, state.getB1()); + this.setB4(state, state.getB1()); } @Override @@ -280,7 +281,7 @@ public class ACCTAPI implements API { @Override public void payAmountToB(long value1, MachineState state) { - char firstChar = String.format("%c", state.b1).charAt(0); + char firstChar = String.format("%c", state.getB1()).charAt(0); Account recipient = this.accounts.values().stream().filter((account) -> account.address.charAt(0) == firstChar).findFirst().get(); recipient.balance += value1; System.out.println("Paid " + value1 + " to " + recipient.address + ", their balance now: " + recipient.balance); diff --git a/Java/tests/common/ExecutableTest.java b/Java/tests/common/ExecutableTest.java new file mode 100644 index 0000000..6a2144c --- /dev/null +++ b/Java/tests/common/ExecutableTest.java @@ -0,0 +1,124 @@ +package common; + +import static common.TestUtils.hexToBytes; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.security.Security; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.ciyam.at.MachineState; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; + +public abstract class ExecutableTest { + + public static final int CODE_OFFSET = 6 * 2; + public static final int DATA_OFFSET = CODE_OFFSET + 0x0200; + public static final int CALL_STACK_OFFSET = DATA_OFFSET + 0x0020 * 8; + + public TestLogger logger; + public TestAPI api; + public MachineState state; + public ByteBuffer codeByteBuffer; + public ByteBuffer stateByteBuffer; + public int callStackSize; + public int userStackOffset; + public int userStackSize; + + @BeforeClass + public static void beforeClass() { + Security.insertProviderAt(new BouncyCastleProvider(), 0); + } + + @Before + public void beforeTest() { + logger = new TestLogger(); + api = new TestAPI(); + codeByteBuffer = ByteBuffer.allocate(512).order(ByteOrder.LITTLE_ENDIAN); + stateByteBuffer = null; + } + + @After + public void afterTest() { + stateByteBuffer = null; + codeByteBuffer = null; + api = null; + logger = null; + } + + protected void execute(boolean onceOnly) { + // version 0003, reserved 0000, code 0200 * 1, data 0020 * 8, call stack 0010 * 4, user stack 0010 * 8 + byte[] headerBytes = hexToBytes("0300" + "0000" + "0002" + "2000" + "1000" + "1000"); + byte[] codeBytes = codeByteBuffer.array(); + byte[] dataBytes = new byte[0]; + + state = new MachineState(api, logger, headerBytes, codeBytes, dataBytes); + + do { + System.out.println("Starting execution:"); + System.out.println("Current block height: " + api.getCurrentBlockHeight()); + + // Actual execution + state.execute(); + + System.out.println("After execution:"); + System.out.println("Steps: " + state.getSteps()); + System.out.println("Program Counter: " + String.format("%04x", state.getProgramCounter())); + System.out.println("Stop Address: " + String.format("%04x", state.getOnStopAddress())); + System.out.println("Error Address: " + (state.getOnErrorAddress() == null ? "not set" : String.format("%04x", state.getOnErrorAddress()))); + + if (state.getIsSleeping()) + System.out.println("Sleeping until current block height (" + state.getCurrentBlockHeight() + ") reaches " + state.getSleepUntilHeight()); + else + System.out.println("Sleeping: " + state.getIsSleeping()); + + System.out.println("Stopped: " + state.getIsStopped()); + System.out.println("Finished: " + state.getIsFinished()); + + if (state.getHadFatalError()) + System.out.println("Finished due to fatal error!"); + + System.out.println("Frozen: " + state.getIsFrozen()); + + // Bump block height + api.bumpCurrentBlockHeight(); + } while (!onceOnly && !state.getIsFinished()); + + // Ready for diagnosis + byte[] stateBytes = state.toBytes(); + + // We know how the state will be serialized so we can extract values + // header(6) + code(0x0200) + data(0x0020 * 8) + callStack length(4) + callStack + userStack length(4) + userStack + + stateByteBuffer = ByteBuffer.wrap(stateBytes).order(ByteOrder.LITTLE_ENDIAN); + callStackSize = stateByteBuffer.getInt(CALL_STACK_OFFSET); + userStackOffset = CALL_STACK_OFFSET + 4 + callStackSize; + userStackSize = stateByteBuffer.getInt(userStackOffset); + } + + protected long getData(int address) { + int index = DATA_OFFSET + address * MachineState.VALUE_SIZE; + return stateByteBuffer.getLong(index); + } + + protected int getCallStackPosition() { + return 0x0010 * MachineState.ADDRESS_SIZE - callStackSize; + } + + protected int getCallStackEntry(int address) { + int index = CALL_STACK_OFFSET + 4 + address - 0x0010 * MachineState.ADDRESS_SIZE + callStackSize; + return stateByteBuffer.getInt(index); + } + + protected int getUserStackPosition() { + return 0x0010 * MachineState.VALUE_SIZE - userStackSize; + } + + protected long getUserStackEntry(int address) { + int index = userStackOffset + 4 + address - 0x0010 * MachineState.VALUE_SIZE + userStackSize; + return stateByteBuffer.getLong(index); + } + +} diff --git a/Java/tests/common/TestAPI.java b/Java/tests/common/TestAPI.java index c2aa819..fb711b1 100644 --- a/Java/tests/common/TestAPI.java +++ b/Java/tests/common/TestAPI.java @@ -7,13 +7,23 @@ import org.ciyam.at.IllegalFunctionCodeException; import org.ciyam.at.MachineState; import org.ciyam.at.Timestamp; -public class TestAPI implements API { +public class TestAPI extends API { private static final int BLOCK_PERIOD = 10 * 60; // average period between blocks in seconds + private int currentBlockHeight; + + public TestAPI() { + this.currentBlockHeight = 10; + } + + public void bumpCurrentBlockHeight() { + ++this.currentBlockHeight; + } + @Override public int getCurrentBlockHeight() { - return 10; + return this.currentBlockHeight; } @Override @@ -23,19 +33,19 @@ public class TestAPI implements API { @Override public void putPreviousBlockHashInA(MachineState state) { - state.a1 = 9L; - state.a2 = 9L; - state.a3 = 9L; - state.a4 = 9L; + this.setA1(state, 9L); + this.setA2(state, 9L); + this.setA3(state, 9L); + this.setA4(state, 9L); } @Override public void putTransactionAfterTimestampInA(Timestamp timestamp, MachineState state) { // Cycle through transactions: 1 -> 2 -> 3 -> 0 -> 1 ... - state.a1 = (timestamp.transactionSequence + 1) % 4; - state.a2 = state.a1; - state.a3 = state.a1; - state.a4 = state.a1; + this.setA1(state, (timestamp.transactionSequence + 1) % 4); + this.setA2(state, state.getA1()); + this.setA3(state, state.getA1()); + this.setA4(state, state.getA1()); } @Override @@ -55,13 +65,13 @@ public class TestAPI implements API { @Override public long generateRandomUsingTransactionInA(MachineState state) { - if (state.steps != 0) { + if (state.getSteps() != 0) { // First call System.out.println("generateRandomUsingTransactionInA: first call - sleeping"); - // Perform init? + // first-call initialization would go here - state.isSleeping = true; + this.setIsSleeping(state, true); return 0L; // not used } else { @@ -69,34 +79,34 @@ public class TestAPI implements API { System.out.println("generateRandomUsingTransactionInA: second call - returning random"); // HASH(A and new block hash) - return (state.a1 ^ 9L) << 3 ^ (state.a2 ^ 9L) << 12 ^ (state.a3 ^ 9L) << 5 ^ (state.a4 ^ 9L); + return (state.getA1() ^ 9L) << 3 ^ (state.getA2() ^ 9L) << 12 ^ (state.getA3() ^ 9L) << 5 ^ (state.getA4() ^ 9L); } } @Override public void putMessageFromTransactionInAIntoB(MachineState state) { - state.b1 = state.a4; - state.b2 = state.a3; - state.b3 = state.a2; - state.b4 = state.a1; + this.setB1(state, state.getA4()); + this.setB2(state, state.getA3()); + this.setB3(state, state.getA2()); + this.setB4(state, state.getA1()); } @Override public void putAddressFromTransactionInAIntoB(MachineState state) { // Dummy address - state.b1 = 0xaaaaaaaaaaaaaaaaL; - state.b2 = 0xaaaaaaaaaaaaaaaaL; - state.b3 = 0xaaaaaaaaaaaaaaaaL; - state.b4 = 0xaaaaaaaaaaaaaaaaL; + this.setB1(state, 0xaaaaaaaaaaaaaaaaL); + this.setB2(state, 0xaaaaaaaaaaaaaaaaL); + this.setB3(state, 0xaaaaaaaaaaaaaaaaL); + this.setB4(state, 0xaaaaaaaaaaaaaaaaL); } @Override public void putCreatorAddressIntoB(MachineState state) { // Dummy creator - state.b1 = 0xccccccccccccccccL; - state.b2 = 0xccccccccccccccccL; - state.b3 = 0xccccccccccccccccL; - state.b4 = 0xccccccccccccccccL; + this.setB1(state, 0xccccccccccccccccL); + this.setB2(state, 0xccccccccccccccccL); + this.setB3(state, 0xccccccccccccccccL); + this.setB4(state, 0xccccccccccccccccL); } @Override diff --git a/Java/tests/common/TestUtils.java b/Java/tests/common/TestUtils.java index 40a4cea..ca1eaf8 100644 --- a/Java/tests/common/TestUtils.java +++ b/Java/tests/common/TestUtils.java @@ -2,8 +2,16 @@ package common; import java.math.BigInteger; +import org.ciyam.at.MachineState; + public class TestUtils { + // v3 constants replicated due to private cope in MachineState + public static final int CODE_PAGE_SIZE = 1; + public static final int DATA_PAGE_SIZE = MachineState.VALUE_SIZE; + public static final int CALL_STACK_PAGE_SIZE = MachineState.ADDRESS_SIZE; + public static final int USER_STACK_PAGE_SIZE = MachineState.VALUE_SIZE; + public static byte[] hexToBytes(String hex) { byte[] output = new byte[hex.length() / 2]; byte[] converted = new BigInteger("00" + hex, 16).toByteArray();