mirror of
https://github.com/Qortal/AT.git
synced 2025-01-30 02:42:14 +00:00
Conversion to big-endian. Hashing functions use 2 data, not A.
Conversion to big-endian to allow reuse of hash output without having to swap endian. e.g. saving output of HASH160 into data segment, and then hashing more of data segment to produce P2SH address. Also changed hashing functions to fetch data start address and data byte length from data segment, rather than loading values into A. Tidied up some tests. Remove obsolete ACCT test.
This commit is contained in:
parent
36029c132f
commit
92281a1d04
@ -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();
|
||||
|
@ -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<br>
|
||||
* <tt>0x0200</tt>
|
||||
* MD5 message data starts at address in A1 and byte-length is in A2.<br>
|
||||
* MD5 data into B<br>
|
||||
* <tt>0x0200 start-addr byte-length</tt>
|
||||
* 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<br>
|
||||
* <tt>0x0201</tt><br>
|
||||
* MD5 message data starts at address in A1 and byte-length is in A2.<br>
|
||||
* Check MD5 of data matches B<br>
|
||||
* <tt>0x0201 start-addr byte-length</tt><br>
|
||||
* 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<br>
|
||||
* <tt>0x0202</tt>
|
||||
* RIPE-MD160 message data starts at address in A1 and byte-length is in A2.<br>
|
||||
* RIPE-MD160 hash stored in B1, B2 and LSB of B3. B4 is zeroed.
|
||||
* RIPE-MD160 data into B<br>
|
||||
* <tt>0x0202 start-addr byte-length</tt>
|
||||
* 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<br>
|
||||
* <tt>0x0203</tt><br>
|
||||
* RIPE-MD160 message data starts at address in A1 and byte-length is in A2.<br>
|
||||
* Other RIPE-MD160 hash is in B1, B2 and LSB of B3. B4 is ignored.
|
||||
* Check RIPE-MD160 of data matches B<br>
|
||||
* <tt>0x0203 start-addr byte-length</tt><br>
|
||||
* 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<br>
|
||||
* <tt>0x0204</tt>
|
||||
* SHA256 message data starts at address in A1 and byte-length is in A2.<br>
|
||||
* SHA256 data into B<br>
|
||||
* <tt>0x0204 start-addr byte-length</tt>
|
||||
* 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<br>
|
||||
* <tt>0x0205</tt><br>
|
||||
* SHA256 message data starts at address in A1 and byte-length is in A2.<br>
|
||||
* Check SHA256 of data matches B<br>
|
||||
* <tt>0x0205 start-addr byte-length</tt><br>
|
||||
* 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<br>
|
||||
* <tt>0x0206</tt>
|
||||
* HASH160 data into B<br>
|
||||
* <tt>0x0206 start-addr byte-length</tt>
|
||||
* Bitcoin's HASH160 hash is equivalent to RMD160(SHA256(data)).<br>
|
||||
* HASH160 message data starts at address in A1 and byte-length is in A2.<br>
|
||||
* 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<br>
|
||||
* <tt>0x0207</tt><br>
|
||||
* HASH160 message data starts at address in A1 and byte-length is in A2.<br>
|
||||
* Check HASH160 of data matches B<br>
|
||||
* <tt>0x0207 start-addr byte-length</tt><br>
|
||||
* 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];
|
||||
|
||||
|
@ -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) };
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
Security.insertProviderAt(new BouncyCastleProvider(), 0);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void beforeTest() {
|
||||
logger = new TestLogger();
|
||||
api = new TestAPI();
|
||||
codeByteBuffer = ByteBuffer.allocate(512).order(ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
|
||||
@After
|
||||
public void afterTest() {
|
||||
codeByteBuffer = null;
|
||||
api = null;
|
||||
logger = null;
|
||||
}
|
||||
private static final String message = "The quick, brown fox jumped over the lazy dog.";
|
||||
private static final byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
@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"));
|
||||
public void testRMD160disassembly() 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);
|
||||
|
||||
// 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_RET_DAT_2.value).putShort(FunctionCode.CHECK_RMD160_WITH_B.value).putInt(1).putInt(2).putInt(3);
|
||||
|
||||
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[] headerBytes = TestUtils.HEADER_BYTES;
|
||||
byte[] codeBytes = codeByteBuffer.array();
|
||||
byte[] dataBytes = new byte[0];
|
||||
|
||||
state = new MachineState(api, logger, headerBytes, codeBytes, dataBytes);
|
||||
|
||||
System.out.println(state.disassemble());
|
||||
}
|
||||
|
||||
@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[] codeBytes = codeByteBuffer.array();
|
||||
byte[] dataBytes = new byte[0];
|
||||
byte[] dataBytes = dataByteBuffer.array();
|
||||
|
||||
state = new MachineState(api, logger, headerBytes, codeBytes, dataBytes);
|
||||
|
||||
|
@ -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,32 @@ 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
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
// Actual hash function
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_DAT_2.value).putShort(hashFunction.value).putInt(2).putInt(3);
|
||||
|
||||
// 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);
|
||||
|
||||
// A3 unused
|
||||
// A4 unused
|
||||
|
||||
// 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
|
||||
|
||||
final FunctionCode bSettingFunction = bSettingFunctions[bWord];
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(bSettingFunction.value).putInt(0);
|
||||
}
|
||||
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(checkFunction.value).putInt(1);
|
||||
// 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);
|
||||
|
||||
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||
|
||||
@ -160,4 +143,56 @@ public class FunctionCodeTests extends ExecutableTest {
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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];
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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));
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user