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(); + } + }