mirror of
https://github.com/Qortal/AT.git
synced 2025-01-30 02:42:14 +00:00
OpCode refactoring, versioned constants, refactored tests
API is now an abstract class instead of an Interface. This is to allow protected methods that give access to package-scoped methods and variables inside MachineState. MachineState now supports a different set of constants based on AT version. MachineState's methods and variables have had their scopes tightened up and getters/setters added where appropriate. MachineState.parseHeader() inlined into the constructor that calls it so it can set public final variables. Some reordering and additional comments in MachineState. OpCode enum entries refactored so that: a) variables provided via MachineState "state" are not explicitly passed as well b) args to each opcode are pre-fetched from the codeByteBuffer before calling and only need to be cast before use. this comes almost "for free" thanks to OpCodeParam.fetch Calling functions from OpCode now cleaner as there's no write-access to programCounter, only changing codeByteBuffer's position. Also in OpCode, state.getProgramCounter() now provides a consistent before-opcode position for branches, jumps, etc. Lots of repeated code refactored out of unit tests into ExecutableTest class. ExecutableTest also provides helper methods for examining post-execution data values, stack positions and entries.
This commit is contained in:
parent
f0e031599d
commit
454d4bed35
@ -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 {
|
||||
* <p>
|
||||
* 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 <tt>minutes</tt> of blocks added to 'timestamp'
|
||||
* <p>
|
||||
* <tt>minutes</tt> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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<Short, VersionedConstants> VERSIONED_CONSTANTS = new HashMap<Short, VersionedConstants>();
|
||||
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<String> dataLabels, Map<Integer, String> codeLabels) {
|
||||
/** Return disassembly of code bytes */
|
||||
public String disassemble() throws ExecutionException {
|
||||
String output = "";
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
124
Java/tests/common/ExecutableTest.java
Normal file
124
Java/tests/common/ExecutableTest.java
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user