diff --git a/Java/src/main/java/org/ciyam/at/API.java b/Java/src/main/java/org/ciyam/at/API.java
index 07bd3e4..7841d3b 100644
--- a/Java/src/main/java/org/ciyam/at/API.java
+++ b/Java/src/main/java/org/ciyam/at/API.java
@@ -4,7 +4,6 @@ import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toMap;
import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
import java.util.Map;
/**
@@ -191,9 +190,7 @@ public abstract class API {
}
public void setA(MachineState state, byte[] bytes) {
- // Enforce endian
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
- byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
state.a1 = byteBuffer.getLong();
state.a2 = byteBuffer.getLong();
@@ -218,9 +215,7 @@ public abstract class API {
}
public void setB(MachineState state, byte[] bytes) {
- // Enforce endian
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
- byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
state.b1 = byteBuffer.getLong();
state.b2 = byteBuffer.getLong();
diff --git a/Java/src/main/java/org/ciyam/at/FunctionCode.java b/Java/src/main/java/org/ciyam/at/FunctionCode.java
index c37c284..d03b4e1 100644
--- a/Java/src/main/java/org/ciyam/at/FunctionCode.java
+++ b/Java/src/main/java/org/ciyam/at/FunctionCode.java
@@ -1,7 +1,6 @@
package org.ciyam.at;
import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
@@ -451,22 +450,20 @@ public enum FunctionCode {
}
},
/**
- * MD5 data (offset A1, length A2) into B
- * 0x0200
- * MD5 message data starts at address in A1 and byte-length is in A2.
+ * MD5 data into B
+ * 0x0200 start-addr byte-length
* MD5 hash stored in B1 and B2. B3 and B4 are zeroed.
*/
- MD5_A_TO_B(0x0200, 0, false) {
+ MD5_INTO_B(0x0200, 2, false) {
@Override
protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException {
- byte[] message = getHashData(state);
+ byte[] message = getHashData(functionData, state);
try {
MessageDigest digester = MessageDigest.getInstance("MD5");
byte[] digest = digester.digest(message);
ByteBuffer digestByteBuffer = ByteBuffer.wrap(digest);
- digestByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
state.b1 = digestByteBuffer.getLong();
state.b2 = digestByteBuffer.getLong();
@@ -478,23 +475,21 @@ public enum FunctionCode {
}
},
/**
- * Check MD5 of data (offset A1, length A2) matches B
- * 0x0201
- * MD5 message data starts at address in A1 and byte-length is in A2.
+ * Check MD5 of data matches B
+ * 0x0201 start-addr byte-length
* Other MD5 hash is in B1 and B2. B3 and B4 are ignored.
* Returns 1 if true, 0 if false
*/
- CHECK_MD5_A_WITH_B(0x0201, 0, true) {
+ CHECK_MD5_WITH_B(0x0201, 2, true) {
@Override
protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException {
- byte[] message = getHashData(state);
+ byte[] message = getHashData(functionData, state);
try {
MessageDigest digester = MessageDigest.getInstance("MD5");
byte[] actualDigest = digester.digest(message);
- ByteBuffer digestByteBuffer = ByteBuffer.allocate(2 * MachineState.VALUE_SIZE);
- digestByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
+ ByteBuffer digestByteBuffer = ByteBuffer.allocate(digester.getDigestLength());
digestByteBuffer.putLong(state.b1);
digestByteBuffer.putLong(state.b2);
@@ -511,26 +506,24 @@ public enum FunctionCode {
}
},
/**
- * RIPE-MD160 data (offset A1, length A2) into B
- * 0x0202
- * RIPE-MD160 message data starts at address in A1 and byte-length is in A2.
- * RIPE-MD160 hash stored in B1, B2 and LSB of B3. B4 is zeroed.
+ * RIPE-MD160 data into B
+ * 0x0202 start-addr byte-length
+ * RIPE-MD160 hash stored in LSB of B1 and all of B2 and B3. B4 is zeroed.
*/
- RMD160_A_TO_B(0x0202, 0, false) {
+ RMD160_INTO_B(0x0202, 2, false) {
@Override
protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException {
- byte[] message = getHashData(state);
+ byte[] message = getHashData(functionData, state);
try {
MessageDigest digester = MessageDigest.getInstance("RIPEMD160");
byte[] digest = digester.digest(message);
ByteBuffer digestByteBuffer = ByteBuffer.wrap(digest);
- digestByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
- state.b1 = digestByteBuffer.getLong();
+ state.b1 = (long) digestByteBuffer.getInt() & 0xffffffffL;
state.b2 = digestByteBuffer.getLong();
- state.b3 = (long) digestByteBuffer.getInt() & 0xffffffffL;
+ state.b3 = digestByteBuffer.getLong();
state.b4 = 0L;
} catch (NoSuchAlgorithmException e) {
throw new ExecutionException("No RIPEMD160 message digest service available", e);
@@ -538,27 +531,25 @@ public enum FunctionCode {
}
},
/**
- * Check RIPE-MD160 of data (offset A1, length A2) matches B
- * 0x0203
- * RIPE-MD160 message data starts at address in A1 and byte-length is in A2.
- * Other RIPE-MD160 hash is in B1, B2 and LSB of B3. B4 is ignored.
+ * Check RIPE-MD160 of data matches B
+ * 0x0203 start-addr byte-length
+ * Other RIPE-MD160 hash is in LSB of B1 and all of B2 and B3. B4 is ignored.
* Returns 1 if true, 0 if false
*/
- CHECK_RMD160_A_WITH_B(0x0203, 0, true) {
+ CHECK_RMD160_WITH_B(0x0203, 2, true) {
@Override
protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException {
- byte[] message = getHashData(state);
+ byte[] message = getHashData(functionData, state);
try {
MessageDigest digester = MessageDigest.getInstance("RIPEMD160");
byte[] actualDigest = digester.digest(message);
ByteBuffer digestByteBuffer = ByteBuffer.allocate(digester.getDigestLength());
- digestByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
- digestByteBuffer.putLong(state.b1);
+ digestByteBuffer.putInt((int) (state.b1 & 0xffffffffL));
digestByteBuffer.putLong(state.b2);
- digestByteBuffer.putInt((int) (state.b3 & 0xffffffffL));
+ digestByteBuffer.putLong(state.b3);
// NOTE: b4 ignored
byte[] expectedDigest = digestByteBuffer.array();
@@ -573,22 +564,20 @@ public enum FunctionCode {
}
},
/**
- * SHA256 data (offset A1, length A2) into B
- * 0x0204
- * SHA256 message data starts at address in A1 and byte-length is in A2.
+ * SHA256 data into B
+ * 0x0204 start-addr byte-length
* SHA256 hash is stored in B1 through B4.
*/
- SHA256_A_TO_B(0x0204, 0, false) {
+ SHA256_INTO_B(0x0204, 2, false) {
@Override
protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException {
- byte[] message = getHashData(state);
+ byte[] message = getHashData(functionData, state);
try {
MessageDigest digester = MessageDigest.getInstance("SHA-256");
byte[] digest = digester.digest(message);
ByteBuffer digestByteBuffer = ByteBuffer.wrap(digest);
- digestByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
state.b1 = digestByteBuffer.getLong();
state.b2 = digestByteBuffer.getLong();
@@ -600,23 +589,21 @@ public enum FunctionCode {
}
},
/**
- * Check SHA256 of data (offset A1, length A2) matches B
- * 0x0205
- * SHA256 message data starts at address in A1 and byte-length is in A2.
+ * Check SHA256 of data matches B
+ * 0x0205 start-addr byte-length
* Other SHA256 hash is in B1 through B4.
* Returns 1 if true, 0 if false
*/
- CHECK_SHA256_A_WITH_B(0x0205, 0, true) {
+ CHECK_SHA256_WITH_B(0x0205, 2, true) {
@Override
protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException {
- byte[] message = getHashData(state);
+ byte[] message = getHashData(functionData, state);
try {
MessageDigest digester = MessageDigest.getInstance("SHA-256");
byte[] actualDigest = digester.digest(message);
- ByteBuffer digestByteBuffer = ByteBuffer.allocate(4 * MachineState.VALUE_SIZE);
- digestByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
+ ByteBuffer digestByteBuffer = ByteBuffer.allocate(digester.getDigestLength());
digestByteBuffer.putLong(state.b1);
digestByteBuffer.putLong(state.b2);
@@ -635,16 +622,15 @@ public enum FunctionCode {
}
},
/**
- * HASH160 data (offset A1, length A2) into B
- * 0x0206
+ * HASH160 data into B
+ * 0x0206 start-addr byte-length
* Bitcoin's HASH160 hash is equivalent to RMD160(SHA256(data)).
- * HASH160 message data starts at address in A1 and byte-length is in A2.
- * HASH160 hash stored in B1, B2 and LSB of B3. B4 is zeroed.
+ * HASH160 hash stored in LSB of B1 and all of B2 and B3. B4 is zeroed.
*/
- HASH160_A_TO_B(0x0206, 0, false) {
+ HASH160_INTO_B(0x0206, 2, false) {
@Override
protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException {
- byte[] message = getHashData(state);
+ byte[] message = getHashData(functionData, state);
try {
MessageDigest sha256Digester = MessageDigest.getInstance("SHA-256");
@@ -654,11 +640,10 @@ public enum FunctionCode {
byte[] rmd160Digest = rmd160Digester.digest(sha256Digest);
ByteBuffer digestByteBuffer = ByteBuffer.wrap(rmd160Digest);
- digestByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
- state.b1 = digestByteBuffer.getLong();
+ state.b1 = (long) digestByteBuffer.getInt() & 0xffffffffL;
state.b2 = digestByteBuffer.getLong();
- state.b3 = (long) digestByteBuffer.getInt() & 0xffffffffL;
+ state.b3 = digestByteBuffer.getLong();
state.b4 = 0L;
} catch (NoSuchAlgorithmException e) {
throw new ExecutionException("No SHA-256 or RIPEMD160 message digest service available", e);
@@ -666,16 +651,15 @@ public enum FunctionCode {
}
},
/**
- * Check HASH160 of data (offset A1, length A2) matches B
- * 0x0207
- * HASH160 message data starts at address in A1 and byte-length is in A2.
+ * Check HASH160 of data matches B
+ * 0x0207 start-addr byte-length
* Other HASH160 hash is in B1, B2 and LSB of B3. B4 is ignored.
* Returns 1 if true, 0 if false
*/
- CHECK_HASH160_A_WITH_B(0x0207, 0, true) {
+ CHECK_HASH160_WITH_B(0x0207, 2, true) {
@Override
protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException {
- byte[] message = getHashData(state);
+ byte[] message = getHashData(functionData, state);
try {
MessageDigest sha256Digester = MessageDigest.getInstance("SHA-256");
@@ -685,11 +669,10 @@ public enum FunctionCode {
byte[] rmd160Digest = rmd160Digester.digest(sha256Digest);
ByteBuffer digestByteBuffer = ByteBuffer.allocate(rmd160Digester.getDigestLength());
- digestByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
- digestByteBuffer.putLong(state.b1);
+ digestByteBuffer.putInt((int) (state.b1 & 0xffffffffL));
digestByteBuffer.putLong(state.b2);
- digestByteBuffer.putInt((int) (state.b3 & 0xffffffffL));
+ digestByteBuffer.putLong(state.b3);
// NOTE: b4 ignored
byte[] expectedDigest = digestByteBuffer.array();
@@ -1019,17 +1002,17 @@ public enum FunctionCode {
// TODO: public abstract String disassemble();
- protected byte[] getHashData(MachineState state) throws ExecutionException {
+ protected byte[] getHashData(FunctionData functionData, MachineState state) throws ExecutionException {
// Validate data offset in A1
- if (state.a1 < 0L || state.a1 > Integer.MAX_VALUE || state.a1 >= state.numDataPages)
- throw new ExecutionException("MD5 data offset (A1) out of bounds");
+ if (functionData.value1 < 0L || functionData.value1 > Integer.MAX_VALUE || functionData.value1 >= state.numDataPages)
+ throw new ExecutionException(this.name() + " data start address out of bounds");
// Validate data length in A2
- if (state.a2 < 0L || state.a2 > Integer.MAX_VALUE || state.a1 + byteLengthToDataLength(state.a2) > state.numDataPages)
- throw new ExecutionException("MD5 data length (A2) invalid");
+ if (functionData.value2 < 0L || functionData.value2 > Integer.MAX_VALUE || functionData.value1 + byteLengthToDataLength(functionData.value2) > state.numDataPages)
+ throw new ExecutionException(this.name() + " data length invalid");
- final int dataStart = (int) (state.a1 & 0x7fffffffL);
- final int dataLength = (int) (state.a2 & 0x7fffffffL);
+ final int dataStart = (int) (functionData.value1 & 0x7fffffffL);
+ final int dataLength = (int) (functionData.value2 & 0x7fffffffL);
byte[] message = new byte[dataLength];
diff --git a/Java/src/main/java/org/ciyam/at/MachineState.java b/Java/src/main/java/org/ciyam/at/MachineState.java
index b111637..9e2995a 100644
--- a/Java/src/main/java/org/ciyam/at/MachineState.java
+++ b/Java/src/main/java/org/ciyam/at/MachineState.java
@@ -3,7 +3,6 @@ package org.ciyam.at;
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;
@@ -143,7 +142,6 @@ public class MachineState {
// Parsing header bytes
ByteBuffer byteBuffer = ByteBuffer.wrap(this.headerBytes);
- byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
this.version = byteBuffer.getShort();
if (this.version < 1)
@@ -174,14 +172,14 @@ public class MachineState {
this.minActivationAmount = byteBuffer.getLong();
// Header OK - set up code and data buffers
- this.codeByteBuffer = ByteBuffer.allocate(this.numCodePages * this.constants.CODE_PAGE_SIZE).order(ByteOrder.LITTLE_ENDIAN);
- this.dataByteBuffer = ByteBuffer.allocate(this.numDataPages * this.constants.DATA_PAGE_SIZE).order(ByteOrder.LITTLE_ENDIAN);
+ this.codeByteBuffer = ByteBuffer.allocate(this.numCodePages * this.constants.CODE_PAGE_SIZE);
+ this.dataByteBuffer = ByteBuffer.allocate(this.numDataPages * this.constants.DATA_PAGE_SIZE);
// Set up stacks
- this.callStackByteBuffer = ByteBuffer.allocate(this.numCallStackPages * this.constants.CALL_STACK_PAGE_SIZE).order(ByteOrder.LITTLE_ENDIAN);
+ this.callStackByteBuffer = ByteBuffer.allocate(this.numCallStackPages * this.constants.CALL_STACK_PAGE_SIZE);
this.callStackByteBuffer.position(this.callStackByteBuffer.limit()); // Downward-growing stack, so start at the end
- this.userStackByteBuffer = ByteBuffer.allocate(this.numUserStackPages * this.constants.USER_STACK_PAGE_SIZE).order(ByteOrder.LITTLE_ENDIAN);
+ this.userStackByteBuffer = ByteBuffer.allocate(this.numUserStackPages * this.constants.USER_STACK_PAGE_SIZE);
this.userStackByteBuffer.position(this.userStackByteBuffer.limit()); // Downward-growing stack, so start at the end
this.api = api;
@@ -346,7 +344,6 @@ public class MachineState {
public byte[] getA() {
ByteBuffer byteBuffer = ByteBuffer.allocate(4 * 8);
- byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
byteBuffer.putLong(this.a1);
byteBuffer.putLong(this.a2);
@@ -374,7 +371,6 @@ public class MachineState {
public byte[] getB() {
ByteBuffer byteBuffer = ByteBuffer.allocate(4 * 8);
- byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
byteBuffer.putLong(this.b1);
byteBuffer.putLong(this.b2);
@@ -455,7 +451,6 @@ public class MachineState {
byte[] creationBytes = new byte[creationBytesLength];
ByteBuffer byteBuffer = ByteBuffer.wrap(creationBytes);
- byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
// Header bytes:
@@ -574,7 +569,7 @@ public class MachineState {
/** For restoring a previously serialized machine state */
public static MachineState fromBytes(API api, LoggerInterface logger, byte[] bytes, byte[] codeBytes) {
- ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
+ ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
byte[] headerBytes = new byte[HEADER_LENGTH];
byteBuffer.get(headerBytes);
@@ -680,15 +675,15 @@ public class MachineState {
}
}
- /** Convert int to little-endian byte array */
+ /** Convert int to big-endian byte array */
private byte[] toByteArray(int value) {
- return new byte[] { (byte) (value), (byte) (value >> 8), (byte) (value >> 16), (byte) (value >> 24) };
+ return new byte[] { (byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) (value) };
}
- /** Convert long to little-endian byte array */
+ /** Convert long to big-endian byte array */
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) };
+ return new byte[] { (byte) (value >> 56), (byte) (value >> 48), (byte) (value >> 40), (byte) (value >> 32),
+ (byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) (value) };
}
/**
diff --git a/Java/src/test/java/CallStackOpCodeTests.java b/Java/src/test/java/CallStackOpCodeTests.java
index c010070..b08ba9d 100644
--- a/Java/src/test/java/CallStackOpCodeTests.java
+++ b/Java/src/test/java/CallStackOpCodeTests.java
@@ -1,4 +1,3 @@
-import static common.TestUtils.*;
import static org.junit.Assert.*;
import org.ciyam.at.ExecutionException;
@@ -28,7 +27,7 @@ public class CallStackOpCodeTests extends ExecutableTest {
assertTrue(state.getIsFinished());
assertFalse(state.getHadFatalError());
- int expectedCallStackPosition = (state.numCallStackPages - 1) * CALL_STACK_PAGE_SIZE;
+ int expectedCallStackPosition = (state.numCallStackPages - 1) * MachineState.ADDRESS_SIZE;
assertEquals("Call stack pointer incorrect", expectedCallStackPosition, getCallStackPosition());
assertEquals("Return address does not match", returnAddress, getCallStackEntry(expectedCallStackPosition));
@@ -62,7 +61,7 @@ public class CallStackOpCodeTests extends ExecutableTest {
assertTrue(state.getIsFinished());
assertFalse(state.getHadFatalError());
- int expectedCallStackPosition = (state.numCallStackPages - 1 - 1) * CALL_STACK_PAGE_SIZE;
+ int expectedCallStackPosition = (state.numCallStackPages - 1 - 1) * MachineState.ADDRESS_SIZE;
assertEquals("Call stack pointer incorrect", expectedCallStackPosition, getCallStackPosition());
assertEquals("Return address does not match", returnAddress2, getCallStackEntry(expectedCallStackPosition));
@@ -106,7 +105,7 @@ public class CallStackOpCodeTests extends ExecutableTest {
assertTrue(state.getIsFinished());
assertFalse(state.getHadFatalError());
- int expectedCallStackPosition = (state.numCallStackPages - 1 + 1) * CALL_STACK_PAGE_SIZE;
+ int expectedCallStackPosition = (state.numCallStackPages - 1 + 1) * MachineState.ADDRESS_SIZE;
assertEquals("Call stack pointer incorrect", expectedCallStackPosition, getCallStackPosition());
assertEquals("Return address not cleared", 0L, getCallStackEntry(expectedCallStackPosition - MachineState.ADDRESS_SIZE));
@@ -143,7 +142,7 @@ public class CallStackOpCodeTests extends ExecutableTest {
assertTrue(state.getIsFinished());
assertFalse(state.getHadFatalError());
- int expectedCallStackPosition = (state.numCallStackPages - 1 - 1 + 1 + 1) * CALL_STACK_PAGE_SIZE;
+ int expectedCallStackPosition = (state.numCallStackPages - 1 - 1 + 1 + 1) * MachineState.ADDRESS_SIZE;
assertEquals("Call stack pointer incorrect", expectedCallStackPosition, getCallStackPosition());
assertEquals("Return address not cleared", 0L, getCallStackEntry(expectedCallStackPosition - MachineState.ADDRESS_SIZE));
diff --git a/Java/src/test/java/DisassemblyTests.java b/Java/src/test/java/DisassemblyTests.java
index 01887de..315dc80 100644
--- a/Java/src/test/java/DisassemblyTests.java
+++ b/Java/src/test/java/DisassemblyTests.java
@@ -1,90 +1,54 @@
import static common.TestUtils.hexToBytes;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.security.Security;
+import java.nio.charset.StandardCharsets;
-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;
+import common.TestUtils;
-public class DisassemblyTests {
+public class DisassemblyTests extends ExecutableTest {
- public TestLogger logger;
- public API api;
- public MachineState state;
- public ByteBuffer codeByteBuffer;
+ private static final String message = "The quick, brown fox jumped over the lazy dog.";
+ private static final byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
- @BeforeClass
- public static void beforeClass() {
- Security.insertProviderAt(new BouncyCastleProvider(), 0);
- }
+ @Test
+ public void testRMD160disassembly() throws ExecutionException {
+ // Data addr 0 for setting values
+ dataByteBuffer.putLong(0L);
+ // Data addr 1 for results
+ dataByteBuffer.putLong(0L);
- @Before
- public void beforeTest() {
- logger = new TestLogger();
- api = new TestAPI();
- codeByteBuffer = ByteBuffer.allocate(512).order(ByteOrder.LITTLE_ENDIAN);
- }
+ // Data addr 2 has start of message bytes (address 4)
+ dataByteBuffer.putLong(4L);
- @After
- public void afterTest() {
- codeByteBuffer = null;
- api = null;
- logger = null;
- }
+ // Data addr 3 has length of message bytes
+ dataByteBuffer.putLong(messageBytes.length);
- @Test
- public void testMD160disassembly() throws ExecutionException {
- // MD160 of ffffffffffffffffffffffffffffffffffffffffffffffff is 90e735014ea23aa89190121b229c06d58fc71e83
- codeByteBuffer.put(OpCode.SET_VAL.value).putInt(0).put(hexToBytes("ffffffffffffffff"));
- codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_A1.value).putInt(0);
- codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_A2.value).putInt(0);
- codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_A3.value).putInt(0);
- codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_A4.value).putInt(0);
- codeByteBuffer.put(OpCode.SET_VAL.value).putInt(0).put(hexToBytes("90e735014ea23aa8"));
+ // Data addr 4+ for message
+ dataByteBuffer.put(messageBytes);
+
+ // RMD160 of "The quick, brown fox jumped over the lazy dog." is b5a4b1898af3745dbbb5becb83e72787df9952c9
+ codeByteBuffer.put(OpCode.SET_VAL.value).putInt(0).put(hexToBytes("00000000b5a4b189"));
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B1.value).putInt(0);
- codeByteBuffer.put(OpCode.SET_VAL.value).putInt(0).put(hexToBytes("9190121b229c06d5"));
+ codeByteBuffer.put(OpCode.SET_VAL.value).putInt(0).put(hexToBytes("8af3745dbbb5becb"));
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B2.value).putInt(0);
- codeByteBuffer.put(OpCode.SET_VAL.value).putInt(0).put(hexToBytes("8fc71e8300000000"));
+ codeByteBuffer.put(OpCode.SET_VAL.value).putInt(0).put(hexToBytes("83e72787df9952c9"));
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B3.value).putInt(0);
- codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.CHECK_RMD160_A_WITH_B.value).putInt(1);
- codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.ECHO.value).putInt(1);
- codeByteBuffer.put(OpCode.FIN_IMD.value);
- // version 0002, reserved 0000, code 0200 * 1, data 0020 * 8, call stack 0010 * 4, user stack 0010 * 4, minActivation = 0
- byte[] headerBytes = hexToBytes("0200" + "0000" + "0002" + "2000" + "1000" + "1000" + "0000000000000000");
- byte[] codeBytes = codeByteBuffer.array();
- byte[] dataBytes = new byte[0];
+ codeByteBuffer.put(OpCode.EXT_FUN_RET_DAT_2.value).putShort(FunctionCode.CHECK_RMD160_WITH_B.value).putInt(1).putInt(2).putInt(3);
- state = new MachineState(api, logger, headerBytes, codeBytes, dataBytes);
+ codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.ECHO.value).putInt(1);
- System.out.println(state.disassemble());
- }
+ codeByteBuffer.put(OpCode.FIN_IMD.value);
- @Test
- public void testACCTdisassembly() throws ExecutionException {
- codeByteBuffer.put(hexToBytes("3501030900000006040000000900000029302009000000040000000f1ab4000000330403090000003525010a000000260a00"));
- codeByteBuffer.put(hexToBytes("0000320903350703090000003526010a0000001b0a000000cd32280133160100000000331701010000003318010200000033"));
- codeByteBuffer.put(hexToBytes("1901030000003505020a0000001b0a000000a1320b033205041e050000001833000509000000320a033203041ab400000033"));
- codeByteBuffer.put(hexToBytes("160105000000331701060000003318010700000033190108000000320304320b033203041ab7000000000000000000000000"));
- codeByteBuffer.put(hexToBytes("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"));
- codeByteBuffer.put(hexToBytes("000000000000"));
-
- // version 0002, reserved 0000, code 0200 * 1, data 0020 * 8, call stack 0010 * 4, user stack 0010 * 4, minActivation = 0
- byte[] headerBytes = hexToBytes("0200" + "0000" + "0002" + "2000" + "1000" + "1000" + "0000000000000000");
+ byte[] headerBytes = TestUtils.HEADER_BYTES;
byte[] codeBytes = codeByteBuffer.array();
- byte[] dataBytes = new byte[0];
+ byte[] dataBytes = dataByteBuffer.array();
state = new MachineState(api, logger, headerBytes, codeBytes, dataBytes);
diff --git a/Java/src/test/java/FunctionCodeTests.java b/Java/src/test/java/FunctionCodeTests.java
index 934a763..2335673 100644
--- a/Java/src/test/java/FunctionCodeTests.java
+++ b/Java/src/test/java/FunctionCodeTests.java
@@ -20,42 +20,42 @@ public class FunctionCodeTests extends ExecutableTest {
@Test
public void testMD5() throws ExecutionException {
- testHash("MD5", FunctionCode.MD5_A_TO_B, FunctionCode.CHECK_A_EQUALS_B, "1388a82384756096e627e3671e2624bf");
+ testHash("MD5", FunctionCode.MD5_INTO_B, "1388a82384756096e627e3671e2624bf");
}
@Test
public void testCHECK_MD5() throws ExecutionException {
- testHash("MD5", null, FunctionCode.CHECK_MD5_A_WITH_B, "1388a82384756096e627e3671e2624bf");
+ checkHash("MD5", FunctionCode.CHECK_MD5_WITH_B, "1388a82384756096e627e3671e2624bf");
}
@Test
public void testRMD160() throws ExecutionException {
- testHash("RIPE-MD160", FunctionCode.RMD160_A_TO_B, FunctionCode.CHECK_A_EQUALS_B, "b5a4b1898af3745dbbb5becb83e72787df9952c9");
+ testHash("RIPE-MD160", FunctionCode.RMD160_INTO_B, "b5a4b1898af3745dbbb5becb83e72787df9952c9");
}
@Test
public void testCHECK_RMD160() throws ExecutionException {
- testHash("RIPE-MD160", null, FunctionCode.CHECK_RMD160_A_WITH_B, "b5a4b1898af3745dbbb5becb83e72787df9952c9");
+ checkHash("RIPE-MD160", FunctionCode.CHECK_RMD160_WITH_B, "b5a4b1898af3745dbbb5becb83e72787df9952c9");
}
@Test
public void testSHA256() throws ExecutionException {
- testHash("SHA256", FunctionCode.SHA256_A_TO_B, FunctionCode.CHECK_A_EQUALS_B, "c01d63749ebe5d6b16f7247015cac2e49a5ac4fb6c7f24bed07b8aa904da97f3");
+ testHash("SHA256", FunctionCode.SHA256_INTO_B, "c01d63749ebe5d6b16f7247015cac2e49a5ac4fb6c7f24bed07b8aa904da97f3");
}
@Test
public void testCHECK_SHA256() throws ExecutionException {
- testHash("SHA256", null, FunctionCode.CHECK_SHA256_A_WITH_B, "c01d63749ebe5d6b16f7247015cac2e49a5ac4fb6c7f24bed07b8aa904da97f3");
+ checkHash("SHA256", FunctionCode.CHECK_SHA256_WITH_B, "c01d63749ebe5d6b16f7247015cac2e49a5ac4fb6c7f24bed07b8aa904da97f3");
}
@Test
public void testHASH160() throws ExecutionException {
- testHash("HASH160", FunctionCode.HASH160_A_TO_B, FunctionCode.CHECK_A_EQUALS_B, "54d54a03fd447996ab004dee87fab80bf9477e23");
+ testHash("HASH160", FunctionCode.HASH160_INTO_B, "54d54a03fd447996ab004dee87fab80bf9477e23");
}
@Test
public void testCHECK_HASH160() throws ExecutionException {
- testHash("HASH160", null, FunctionCode.CHECK_HASH160_A_WITH_B, "54d54a03fd447996ab004dee87fab80bf9477e23");
+ checkHash("HASH160", FunctionCode.CHECK_HASH160_WITH_B, "54d54a03fd447996ab004dee87fab80bf9477e23");
}
@Test
@@ -107,49 +107,61 @@ public class FunctionCodeTests extends ExecutableTest {
assertTrue(state.getHadFatalError());
}
- private void testHash(String hashName, FunctionCode hashFunction, FunctionCode checkFunction, String expected) throws ExecutionException {
+ private void testHash(String hashName, FunctionCode hashFunction, String expected) throws ExecutionException {
// Data addr 0 for setting values
dataByteBuffer.putLong(0L);
// Data addr 1 for results
dataByteBuffer.putLong(0L);
- // Data addr 2+ for message
- dataByteBuffer.put(messageBytes);
+ // Data addr 2 has start of message bytes (address 4)
+ dataByteBuffer.putLong(4L);
- // MD5 data start
- codeByteBuffer.put(OpCode.SET_VAL.value).putInt(0).putLong(2L);
- codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_A1.value).putInt(0);
+ // Data addr 3 has length of message bytes
+ dataByteBuffer.putLong(messageBytes.length);
- // MD5 data length
- codeByteBuffer.put(OpCode.SET_VAL.value).putInt(0).putLong(messageBytes.length);
- codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_A2.value).putInt(0);
+ // Data addr 4+ for message
+ dataByteBuffer.put(messageBytes);
- // A3 unused
- // A4 unused
+ // Actual hash function
+ codeByteBuffer.put(OpCode.EXT_FUN_DAT_2.value).putShort(hashFunction.value).putInt(2).putInt(3);
- // Optional hash function
- if (hashFunction != null) {
- codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(hashFunction.value);
- // Hash functions usually put result into B, but we need it in A
- codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.SWAP_A_AND_B.value);
- }
+ // Hash functions usually put result into B, but we need it in A
+ codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.SWAP_A_AND_B.value);
// Expected result goes into B
- codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.CLEAR_B.value);
- // Each 16 hex-chars (8 bytes) fits into each B word (B1, B2, B3 and B4)
- for (int bWord = 0; bWord < 4 && bWord * 16 < expected.length(); ++bWord) {
- final int beginIndex = bWord * 16;
- final int endIndex = Math.min(expected.length(), beginIndex + 16);
+ loadHashIntoB(expected);
- String hexChars = expected.substring(beginIndex, endIndex);
- codeByteBuffer.put(OpCode.SET_VAL.value).putInt(0).put(hexToBytes(hexChars));
- codeByteBuffer.put(new byte[8 - hexChars.length() / 2]); // pad with zeros
+ // Check actual hash output (in A) with expected result (in B) and save equality output into address 1
+ codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.CHECK_A_EQUALS_B.value).putInt(1);
- final FunctionCode bSettingFunction = bSettingFunctions[bWord];
- codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(bSettingFunction.value).putInt(0);
- }
+ codeByteBuffer.put(OpCode.FIN_IMD.value);
+
+ execute(true);
+
+ assertTrue("MachineState isn't in finished state", state.getIsFinished());
+ assertFalse("MachineState encountered fatal error", state.getHadFatalError());
+ assertEquals(hashName + " hashes do not match", 1L, getData(1));
+ }
+
+ private void checkHash(String hashName, FunctionCode checkFunction, String expected) throws ExecutionException {
+ // Data addr 0 for setting values
+ dataByteBuffer.putLong(0L);
+ // Data addr 1 for results
+ dataByteBuffer.putLong(0L);
+
+ // Data addr 2 has start of message bytes (address 4)
+ dataByteBuffer.putLong(4L);
+
+ // Data addr 3 has length of message bytes
+ dataByteBuffer.putLong(messageBytes.length);
+
+ // Data addr 4+ for message
+ dataByteBuffer.put(messageBytes);
- codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(checkFunction.value).putInt(1);
+ // Expected result goes into B
+ loadHashIntoB(expected);
+
+ codeByteBuffer.put(OpCode.EXT_FUN_RET_DAT_2.value).putShort(checkFunction.value).putInt(1).putInt(2).putInt(3);
codeByteBuffer.put(OpCode.FIN_IMD.value);
@@ -160,4 +172,27 @@ public class FunctionCodeTests extends ExecutableTest {
assertEquals(hashName + " hashes do not match", 1L, getData(1));
}
+ private void loadHashIntoB(String expected) {
+ // Expected result goes into B
+ codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.CLEAR_B.value);
+
+ // Each 16 hex-chars (8 bytes) fits into each B word (B1, B2, B3 and B4)
+ int numLongs = (expected.length() + 15) / 16;
+
+ for (int longIndex = 0; longIndex < numLongs; ++longIndex) {
+ final int endIndex = expected.length() - (numLongs - longIndex - 1) * 16;
+ final int beginIndex = Math.max(0, endIndex - 16);
+
+ String hexChars = expected.substring(beginIndex, endIndex);
+
+ codeByteBuffer.put(OpCode.SET_VAL.value);
+ codeByteBuffer.putInt(0); // addr 0
+ codeByteBuffer.put(new byte[8 - hexChars.length() / 2]); // pad LSB with zeros
+ codeByteBuffer.put(hexToBytes(hexChars));
+
+ final FunctionCode bSettingFunction = bSettingFunctions[longIndex];
+ codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(bSettingFunction.value).putInt(0);
+ }
+ }
+
}
diff --git a/Java/src/test/java/MiscTests.java b/Java/src/test/java/MiscTests.java
index 69ed62d..8e7a625 100644
--- a/Java/src/test/java/MiscTests.java
+++ b/Java/src/test/java/MiscTests.java
@@ -1,4 +1,3 @@
-import static common.TestUtils.hexToBytes;
import static org.junit.Assert.*;
import org.ciyam.at.ExecutionException;
@@ -8,6 +7,7 @@ import org.ciyam.at.OpCode;
import org.junit.Test;
import common.ExecutableTest;
+import common.TestUtils;
public class MiscTests extends ExecutableTest {
@@ -52,17 +52,16 @@ public class MiscTests extends ExecutableTest {
@Test
public void testMinActivation() throws ExecutionException {
- long minActivation = 12345L; // 0x0000000000003039
+ long minActivationAmount = 12345L; // 0x0000000000003039
- // version 0002, reserved 0000, code 0200 * 1, data 0020 * 8, call stack 0010 * 4, user stack 0010 * 4, minActivation = 12345L
- byte[] headerBytes = hexToBytes("0200" + "0000" + "0002" + "2000" + "1000" + "1000" + "3930000000000000");
+ byte[] headerBytes = TestUtils.toHeaderBytes(TestUtils.VERSION, TestUtils.NUM_CODE_PAGES, TestUtils.NUM_DATA_PAGES, TestUtils.NUM_CALL_STACK_PAGES, TestUtils.NUM_USER_STACK_PAGES, minActivationAmount);
byte[] codeBytes = codeByteBuffer.array();
byte[] dataBytes = new byte[0];
state = new MachineState(api, logger, headerBytes, codeBytes, dataBytes);
assertTrue(state.getIsFrozen());
- assertEquals((Long) (minActivation - 1L), state.getFrozenBalance());
+ assertEquals((Long) (minActivationAmount - 1L), state.getFrozenBalance());
}
}
diff --git a/Java/src/test/java/SerializationTests.java b/Java/src/test/java/SerializationTests.java
index 8b620b1..52bec63 100644
--- a/Java/src/test/java/SerializationTests.java
+++ b/Java/src/test/java/SerializationTests.java
@@ -2,7 +2,6 @@ import static common.TestUtils.hexToBytes;
import static org.junit.Assert.*;
import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
import java.util.Arrays;
import org.ciyam.at.ExecutionException;
@@ -15,6 +14,7 @@ import org.junit.Test;
import common.TestAPI;
import common.TestLogger;
+import common.TestUtils;
public class SerializationTests {
@@ -27,7 +27,7 @@ public class SerializationTests {
public void beforeTest() {
logger = new TestLogger();
api = new TestAPI();
- codeByteBuffer = ByteBuffer.allocate(512).order(ByteOrder.LITTLE_ENDIAN);
+ codeByteBuffer = ByteBuffer.allocate(512);
}
@After
@@ -38,8 +38,7 @@ public class SerializationTests {
}
private byte[] simulate() {
- // version 0002, reserved 0000, code 0200 * 1, data 0020 * 8, call stack 0010 * 4, user stack 0010 * 4, minActivation = 0
- byte[] headerBytes = hexToBytes("0200" + "0000" + "0002" + "2000" + "1000" + "1000" + "0000000000000000");
+ byte[] headerBytes = TestUtils.HEADER_BYTES;
byte[] codeBytes = codeByteBuffer.array();
byte[] dataBytes = new byte[0];
diff --git a/Java/src/test/java/TestACCT.java b/Java/src/test/java/TestACCT.java
deleted file mode 100644
index 3bfe5bc..0000000
--- a/Java/src/test/java/TestACCT.java
+++ /dev/null
@@ -1,221 +0,0 @@
-import static common.TestUtils.hexToBytes;
-import static org.junit.Assert.*;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.security.Security;
-import java.util.Arrays;
-
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-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.ACCTAPI;
-import common.TestLogger;
-
-public class TestACCT {
-
- public TestLogger logger;
- public ACCTAPI api;
- public MachineState state;
- public ByteBuffer codeByteBuffer;
- public ByteBuffer dataByteBuffer;
-
- @BeforeClass
- public static void beforeClass() {
- Security.insertProviderAt(new BouncyCastleProvider(), 0);
- }
-
- @Before
- public void beforeTest() {
- logger = new TestLogger();
- api = new ACCTAPI();
- codeByteBuffer = ByteBuffer.allocate(0x0200 * 1).order(ByteOrder.LITTLE_ENDIAN);
- dataByteBuffer = ByteBuffer.allocate(0x0020 * 8).order(ByteOrder.LITTLE_ENDIAN);
- }
-
- @After
- public void afterTest() {
- dataByteBuffer = null;
- codeByteBuffer = null;
- api = null;
- logger = null;
- }
-
- private byte[] simulate() {
- // version 0002, reserved 0000, code 0200 * 1, data 0020 * 8, call stack 0010 * 4, user stack 0010 * 4, minActivation = 0
- byte[] headerBytes = hexToBytes("0200" + "0000" + "0002" + "2000" + "1000" + "1000" + "0000000000000000");
- byte[] codeBytes = codeByteBuffer.array();
- byte[] dataBytes = dataByteBuffer.array();
-
- state = new MachineState(api, logger, headerBytes, codeBytes, dataBytes);
-
- return executeAndCheck(state);
- }
-
- private byte[] continueSimulation(byte[] savedState) {
- byte[] codeBytes = codeByteBuffer.array();
- state = MachineState.fromBytes(api, logger, savedState, codeBytes);
-
- return executeAndCheck(state);
- }
-
- private byte[] executeAndCheck(MachineState state) {
- state.execute();
-
- api.setCurrentBalance(state.getCurrentBalance());
-
- byte[] stateBytes = state.toBytes();
- byte[] codeBytes = state.getCodeBytes();
- MachineState restoredState = MachineState.fromBytes(api, logger, stateBytes, codeBytes);
- byte[] restoredStateBytes = restoredState.toBytes();
- byte[] restoredCodeBytes = state.getCodeBytes();
-
- assertTrue("Serialization->Deserialization->Reserialization error", Arrays.equals(stateBytes, restoredStateBytes));
- assertTrue("Serialization->Deserialization->Reserialization error", Arrays.equals(codeBytes, restoredCodeBytes));
-
- return stateBytes;
- }
-
- @Test
- public void testACCT() throws ExecutionException {
- // DATA
- final int addrHashPart1 = 0x0;
- final int addrHashPart2 = 0x1;
- final int addrHashPart3 = 0x2;
- final int addrHashPart4 = 0x3;
- final int addrAddressPart1 = 0x4;
- final int addrAddressPart2 = 0x5;
- final int addrAddressPart3 = 0x6;
- final int addrAddressPart4 = 0x7;
- final int addrRefundMinutes = 0x8;
- final int addrRefundTimestamp = 0x9;
- final int addrLastTimestamp = 0xa;
- final int addrBlockTimestamp = 0xb;
- final int addrTxType = 0xc;
- final int addrComparator = 0xd;
- final int addrAddressTemp1 = 0xe;
- final int addrAddressTemp2 = 0xf;
- final int addrAddressTemp3 = 0x10;
- final int addrAddressTemp4 = 0x11;
-
- byte[] secret = new byte[32];
- new SecureRandom().nextBytes(secret);
-
- try {
- MessageDigest digester = MessageDigest.getInstance("SHA-256");
- byte[] digest = digester.digest(secret);
-
- dataByteBuffer.put(digest);
- } catch (NoSuchAlgorithmException e) {
- throw new ExecutionException("No SHA-256 message digest service available", e);
- }
-
- // Destination address (based on "R" for "Responder", where "R" is 0x52)
- dataByteBuffer.put(hexToBytes("5200000000000000520000000000000052000000000000005200000000000000"));
-
- // Expiry in minutes (but actually blocks in this test case)
- dataByteBuffer.putLong(8L);
-
- // Code labels
- final int addrTxLoop = 0x36;
- final int addrCheckTx = 0x4b;
- final int addrCheckSender = 0x64;
- final int addrCheckMessage = 0xab;
- final int addrPayout = 0xdf;
- final int addrRefund = 0x102;
-
- int tempPC;
-
- // init:
- codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_CREATION_TIMESTAMP.value).putInt(addrRefundTimestamp);
- codeByteBuffer.put(OpCode.SET_DAT.value).putInt(addrLastTimestamp).putInt(addrRefundTimestamp);
- codeByteBuffer.put(OpCode.EXT_FUN_RET_DAT_2.value).putShort(FunctionCode.ADD_MINUTES_TO_TIMESTAMP.value).putInt(addrRefundTimestamp)
- .putInt(addrRefundTimestamp).putInt(addrRefundMinutes);
- codeByteBuffer.put(OpCode.SET_PCS.value);
-
- // loop:
- codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_BLOCK_TIMESTAMP.value).putInt(addrBlockTimestamp);
- tempPC = codeByteBuffer.position();
- codeByteBuffer.put(OpCode.BLT_DAT.value).putInt(addrBlockTimestamp).putInt(addrRefundTimestamp).put((byte) (addrTxLoop - tempPC));
- codeByteBuffer.put(OpCode.JMP_ADR.value).putInt(addrRefund);
-
- // txloop:
- assertEquals(addrTxLoop, codeByteBuffer.position());
- codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.PUT_TX_AFTER_TIMESTAMP_IN_A.value).putInt(addrLastTimestamp);
- codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.CHECK_A_IS_ZERO.value).putInt(addrComparator);
- tempPC = codeByteBuffer.position();
- codeByteBuffer.put(OpCode.BZR_DAT.value).putInt(addrComparator).put((byte) (addrCheckTx - tempPC));
- codeByteBuffer.put(OpCode.STP_IMD.value);
-
- // checkTx:
- codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_TIMESTAMP_FROM_TX_IN_A.value).putInt(addrLastTimestamp);
- codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_TYPE_FROM_TX_IN_A.value).putInt(addrTxType);
- tempPC = codeByteBuffer.position();
- codeByteBuffer.put(OpCode.BNZ_DAT.value).putInt(addrTxType).put((byte) (addrCheckSender - tempPC));
- codeByteBuffer.put(OpCode.JMP_ADR.value).putInt(addrTxLoop);
-
- // checkSender
- assertEquals(addrCheckSender, codeByteBuffer.position());
- codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.PUT_ADDRESS_FROM_TX_IN_A_INTO_B.value);
- codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_B1.value).putInt(addrAddressTemp1);
- codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_B2.value).putInt(addrAddressTemp2);
- codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_B3.value).putInt(addrAddressTemp3);
- codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_B4.value).putInt(addrAddressTemp4);
- tempPC = codeByteBuffer.position();
- codeByteBuffer.put(OpCode.BNE_DAT.value).putInt(addrAddressTemp1).putInt(addrAddressPart1).put((byte) (addrTxLoop - tempPC));
- tempPC = codeByteBuffer.position();
- codeByteBuffer.put(OpCode.BNE_DAT.value).putInt(addrAddressTemp2).putInt(addrAddressPart2).put((byte) (addrTxLoop - tempPC));
- tempPC = codeByteBuffer.position();
- codeByteBuffer.put(OpCode.BNE_DAT.value).putInt(addrAddressTemp3).putInt(addrAddressPart3).put((byte) (addrTxLoop - tempPC));
- tempPC = codeByteBuffer.position();
- codeByteBuffer.put(OpCode.BNE_DAT.value).putInt(addrAddressTemp4).putInt(addrAddressPart4).put((byte) (addrTxLoop - tempPC));
-
- // checkMessage:
- assertEquals(addrCheckMessage, codeByteBuffer.position());
- codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.PUT_MESSAGE_FROM_TX_IN_A_INTO_B.value);
- codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.SWAP_A_AND_B.value);
- codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B1.value).putInt(addrHashPart1);
- codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B2.value).putInt(addrHashPart2);
- codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B3.value).putInt(addrHashPart3);
- codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B4.value).putInt(addrHashPart4);
- codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.CHECK_SHA256_A_WITH_B.value).putInt(addrComparator);
- tempPC = codeByteBuffer.position();
- codeByteBuffer.put(OpCode.BNZ_DAT.value).putInt(addrComparator).put((byte) (addrPayout - tempPC));
- codeByteBuffer.put(OpCode.JMP_ADR.value).putInt(addrTxLoop);
-
- // payout:
- assertEquals(addrPayout, codeByteBuffer.position());
- codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B1.value).putInt(addrAddressPart1);
- codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B2.value).putInt(addrAddressPart2);
- codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B3.value).putInt(addrAddressPart3);
- codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B4.value).putInt(addrAddressPart4);
- codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.MESSAGE_A_TO_ADDRESS_IN_B.value);
- codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.PAY_ALL_TO_ADDRESS_IN_B.value);
- codeByteBuffer.put(OpCode.FIN_IMD.value);
-
- // refund:
- assertEquals(addrRefund, codeByteBuffer.position());
- codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.PUT_CREATOR_INTO_B.value);
- codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.PAY_ALL_TO_ADDRESS_IN_B.value);
- codeByteBuffer.put(OpCode.FIN_IMD.value);
-
- byte[] savedState = simulate();
-
- while (!state.getIsFinished()) {
- ((ACCTAPI) state.getAPI()).generateNextBlock(secret);
-
- savedState = continueSimulation(savedState);
- }
- }
-
-}
diff --git a/Java/src/test/java/UserStackOpCodeTests.java b/Java/src/test/java/UserStackOpCodeTests.java
index 38ef017..db49724 100644
--- a/Java/src/test/java/UserStackOpCodeTests.java
+++ b/Java/src/test/java/UserStackOpCodeTests.java
@@ -1,7 +1,7 @@
-import static common.TestUtils.*;
import static org.junit.Assert.*;
import org.ciyam.at.ExecutionException;
+import org.ciyam.at.MachineState;
import org.ciyam.at.OpCode;
import org.junit.Test;
@@ -20,7 +20,7 @@ public class UserStackOpCodeTests extends ExecutableTest {
assertTrue(state.getIsFinished());
assertFalse(state.getHadFatalError());
- int expectedUserStackPosition = (state.numUserStackPages - 1) * USER_STACK_PAGE_SIZE;
+ int expectedUserStackPosition = (state.numUserStackPages - 1) * MachineState.VALUE_SIZE;
assertEquals("User stack pointer incorrect", expectedUserStackPosition, getUserStackPosition());
assertEquals("Data does not match", 4444L, getUserStackEntry(expectedUserStackPosition));
}
@@ -38,7 +38,7 @@ public class UserStackOpCodeTests extends ExecutableTest {
assertTrue(state.getIsFinished());
assertFalse(state.getHadFatalError());
- int expectedUserStackPosition = (state.numUserStackPages - 2) * USER_STACK_PAGE_SIZE;
+ int expectedUserStackPosition = (state.numUserStackPages - 2) * MachineState.VALUE_SIZE;
assertEquals("User stack pointer incorrect", expectedUserStackPosition, getUserStackPosition());
assertEquals("Data does not match", 3333L, getUserStackEntry(expectedUserStackPosition));
}
@@ -95,7 +95,7 @@ public class UserStackOpCodeTests extends ExecutableTest {
assertTrue(state.getIsFinished());
assertFalse(state.getHadFatalError());
- int expectedUserStackPosition = (state.numUserStackPages - 1 + 1) * USER_STACK_PAGE_SIZE;
+ int expectedUserStackPosition = (state.numUserStackPages - 1 + 1) * MachineState.VALUE_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:
@@ -117,7 +117,7 @@ public class UserStackOpCodeTests extends ExecutableTest {
assertTrue(state.getIsFinished());
assertFalse(state.getHadFatalError());
- int expectedUserStackPosition = (state.numUserStackPages - 1 - 1 + 1 + 1) * USER_STACK_PAGE_SIZE;
+ int expectedUserStackPosition = (state.numUserStackPages - 1 - 1 + 1 + 1) * MachineState.VALUE_SIZE;
assertEquals("User stack pointer incorrect", expectedUserStackPosition, getUserStackPosition());
assertEquals("Data does not match", 3333L, getData(2));
assertEquals("Data does not match", 4444L, getData(3));
diff --git a/Java/src/test/java/common/ACCTAPI.java b/Java/src/test/java/common/ACCTAPI.java
index de03e2b..7c553cb 100644
--- a/Java/src/test/java/common/ACCTAPI.java
+++ b/Java/src/test/java/common/ACCTAPI.java
@@ -129,17 +129,17 @@ public class ACCTAPI extends API {
this.blockchain.add(block);
}
- /** Convert long to little-endian byte array */
+ /** Convert long to big-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) };
+ return new byte[] { (byte) (value >> 56), (byte) (value >> 48), (byte) (value >> 40), (byte) (value >> 32),
+ (byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) (value) };
}
- /** Convert part of little-endian byte[] to long */
+ /** Convert part of big-endian byte[] to long */
private long fromBytes(byte[] bytes, int start) {
- return (bytes[start] & 0xffL) | (bytes[start + 1] & 0xffL) << 8 | (bytes[start + 2] & 0xffL) << 16 | (bytes[start + 3] & 0xffL) << 24
- | (bytes[start + 4] & 0xffL) << 32 | (bytes[start + 5] & 0xffL) << 40 | (bytes[start + 6] & 0xffL) << 48 | (bytes[start + 7] & 0xffL) << 56;
+ return (bytes[start] & 0xffL) << 56 | (bytes[start + 1] & 0xffL) << 48 | (bytes[start + 2] & 0xffL) << 40 | (bytes[start + 3] & 0xffL) << 32
+ | (bytes[start + 4] & 0xffL) << 24 | (bytes[start + 5] & 0xffL) << 16 | (bytes[start + 6] & 0xffL) << 8 | (bytes[start + 7] & 0xffL);
}
private String getRandomAccount() {
diff --git a/Java/src/test/java/common/ExecutableTest.java b/Java/src/test/java/common/ExecutableTest.java
index 2b41531..6eda3fc 100644
--- a/Java/src/test/java/common/ExecutableTest.java
+++ b/Java/src/test/java/common/ExecutableTest.java
@@ -1,9 +1,6 @@
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;
@@ -14,10 +11,8 @@ import org.junit.BeforeClass;
public abstract class ExecutableTest {
- public static final int CODE_STACK_SIZE = 0x0200;
- public static final int DATA_OFFSET = 6 * 2 + 8;
- public static final int DATA_STACK_SIZE = 0x0200;
- public static final int CALL_STACK_OFFSET = DATA_OFFSET + DATA_STACK_SIZE * 8;
+ private static final int DATA_OFFSET = MachineState.HEADER_LENGTH; // code bytes are not present
+ private static final int CALL_STACK_OFFSET = DATA_OFFSET + TestUtils.NUM_DATA_PAGES * MachineState.VALUE_SIZE;
public TestLogger logger;
public TestAPI api;
@@ -38,8 +33,8 @@ public abstract class ExecutableTest {
public void beforeTest() {
logger = new TestLogger();
api = new TestAPI();
- codeByteBuffer = ByteBuffer.allocate(CODE_STACK_SIZE).order(ByteOrder.LITTLE_ENDIAN);
- dataByteBuffer = ByteBuffer.allocate(DATA_STACK_SIZE).order(ByteOrder.LITTLE_ENDIAN);
+ codeByteBuffer = ByteBuffer.allocate(TestUtils.NUM_CODE_PAGES * MachineState.OPCODE_SIZE);
+ dataByteBuffer = ByteBuffer.allocate(TestUtils.NUM_DATA_PAGES * MachineState.VALUE_SIZE);
stateByteBuffer = null;
}
@@ -53,8 +48,7 @@ public abstract class ExecutableTest {
}
protected void execute(boolean onceOnly) {
- // version 0002, reserved 0000, code 0200 * 1, data 0200 * 8, call stack 0010 * 4, user stack 0010 * 4, minActivation = 0
- byte[] headerBytes = hexToBytes("0200" + "0000" + "0002" + "0002" + "1000" + "1000" + "0000000000000000");
+ byte[] headerBytes = TestUtils.HEADER_BYTES;
byte[] codeBytes = codeByteBuffer.array();
byte[] dataBytes = dataByteBuffer.array();
@@ -98,9 +92,9 @@ public abstract class ExecutableTest {
byte[] stateBytes = state.toBytes();
// We know how the state will be serialized so we can extract values
- // header(6) + data(size * 8) + callStack length(4) + callStack + userStack length(4) + userStack
+ // header + data(size * 8) + callStack length(4) + callStack + userStack length(4) + userStack
- stateByteBuffer = ByteBuffer.wrap(stateBytes).order(ByteOrder.LITTLE_ENDIAN);
+ stateByteBuffer = ByteBuffer.wrap(stateBytes);
callStackSize = stateByteBuffer.getInt(CALL_STACK_OFFSET);
userStackOffset = CALL_STACK_OFFSET + 4 + callStackSize;
userStackSize = stateByteBuffer.getInt(userStackOffset);
@@ -112,20 +106,20 @@ public abstract class ExecutableTest {
}
protected int getCallStackPosition() {
- return 0x0010 * MachineState.ADDRESS_SIZE - callStackSize;
+ return TestUtils.NUM_CALL_STACK_PAGES * MachineState.ADDRESS_SIZE - callStackSize;
}
protected int getCallStackEntry(int address) {
- int index = CALL_STACK_OFFSET + 4 + address - 0x0010 * MachineState.ADDRESS_SIZE + callStackSize;
+ int index = CALL_STACK_OFFSET + 4 + address - TestUtils.NUM_CALL_STACK_PAGES * MachineState.ADDRESS_SIZE + callStackSize;
return stateByteBuffer.getInt(index);
}
protected int getUserStackPosition() {
- return 0x0010 * MachineState.VALUE_SIZE - userStackSize;
+ return TestUtils.NUM_USER_STACK_PAGES * MachineState.VALUE_SIZE - userStackSize;
}
protected long getUserStackEntry(int address) {
- int index = userStackOffset + 4 + address - 0x0010 * MachineState.VALUE_SIZE + userStackSize;
+ int index = userStackOffset + 4 + address - TestUtils.NUM_USER_STACK_PAGES * MachineState.VALUE_SIZE + userStackSize;
return stateByteBuffer.getLong(index);
}
diff --git a/Java/src/test/java/common/TestUtils.java b/Java/src/test/java/common/TestUtils.java
index fb96760..1292510 100644
--- a/Java/src/test/java/common/TestUtils.java
+++ b/Java/src/test/java/common/TestUtils.java
@@ -1,16 +1,19 @@
package common;
import java.math.BigInteger;
+import java.nio.ByteBuffer;
import org.ciyam.at.MachineState;
public class TestUtils {
- // v3 constants replicated due to private scope 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 final short VERSION = 2;
+ public static final short NUM_CODE_PAGES = 0x0200;
+ public static final short NUM_DATA_PAGES = 0x0200;
+ public static final short NUM_CALL_STACK_PAGES = 0x0010;
+ public static final short NUM_USER_STACK_PAGES = 0x0010;
+ public static final long MIN_ACTIVATION_AMOUNT = 0L;
+ public static final byte[] HEADER_BYTES = toHeaderBytes(VERSION, NUM_CODE_PAGES, NUM_DATA_PAGES, NUM_CALL_STACK_PAGES, NUM_USER_STACK_PAGES, MIN_ACTIVATION_AMOUNT);
public static byte[] hexToBytes(String hex) {
byte[] output = new byte[hex.length() / 2];
@@ -26,4 +29,31 @@ public class TestUtils {
return output;
}
+ public static byte[] toHeaderBytes(short version, short numCodePages, short numDataPages, short numCallStackPages, short numUserStackPages, long minActivationAmount) {
+ ByteBuffer byteBuffer = ByteBuffer.allocate(MachineState.HEADER_LENGTH);
+
+ // Version
+ byteBuffer.putShort(version);
+
+ // Reserved
+ byteBuffer.putShort((short) 0);
+
+ // Code length
+ byteBuffer.putShort(numCodePages);
+
+ // Data length
+ byteBuffer.putShort(numDataPages);
+
+ // Call stack length
+ byteBuffer.putShort(numCallStackPages);
+
+ // User stack length
+ byteBuffer.putShort(numUserStackPages);
+
+ // Minimum activation amount
+ byteBuffer.putLong(minActivationAmount);
+
+ return byteBuffer.array();
+ }
+
}