mirror of
https://github.com/Qortal/AT.git
synced 2025-01-30 02:42:14 +00:00
More tests (now in org.ciyam.at), better exception messages, tidying, tightened visibilty.
Added unit test to cover branching to beyond end of code segment. Added unit test to cover branching to before start of code segment. Added very naive fuzzy disassembly test to see how it handles random, invalid code. Refactored data address checking in FunctionCode.postCheckExecute() methods to single FunctionCode.checkDataAddress method. New method includes FunctionCode's name when throwing due to out-of-bounds address. Tidied narrowing conversions by replacing literal 0x7fffffffL with Integer.MAX_VALUE. Tightened OpCode methods visiblity from public to protected. Added branch target checking to OpCode.executeBranchConditional but maybe this is already covered by call to Utils.getCodeOffset() when assembling params in Opcode.execute(). Improved message in Utils.getCodeOffset() anyway. Moved unit tests to org.ciyam.at package. Moved unit test support classes from 'common' to org.ciyam.at.test.
This commit is contained in:
parent
36008bdeac
commit
6e9198b226
@ -121,10 +121,9 @@ public enum FunctionCode {
|
||||
@Override
|
||||
protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException {
|
||||
// Validate data offset in arg1
|
||||
if (functionData.value1 < 0L || functionData.value1 > Integer.MAX_VALUE || functionData.value1 >= state.numDataPages - 3)
|
||||
throw new ExecutionException(this.name() + " data start address out of bounds");
|
||||
checkDataAddress(state, functionData.value1, 4);
|
||||
|
||||
int dataIndex = (int) (functionData.value1 & 0x7fffffffL);
|
||||
int dataIndex = (int) (functionData.value1 & Integer.MAX_VALUE);
|
||||
|
||||
state.dataByteBuffer.putLong(dataIndex++ * MachineState.VALUE_SIZE, state.a1);
|
||||
state.dataByteBuffer.putLong(dataIndex++ * MachineState.VALUE_SIZE, state.a2);
|
||||
@ -140,10 +139,9 @@ public enum FunctionCode {
|
||||
@Override
|
||||
protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException {
|
||||
// Validate data offset in arg1
|
||||
if (functionData.value1 < 0L || functionData.value1 > Integer.MAX_VALUE || functionData.value1 >= state.numDataPages - 3)
|
||||
throw new ExecutionException(this.name() + " data start address out of bounds");
|
||||
checkDataAddress(state, functionData.value1, 4);
|
||||
|
||||
int dataIndex = (int) (functionData.value1 & 0x7fffffffL);
|
||||
int dataIndex = (int) (functionData.value1 & Integer.MAX_VALUE);
|
||||
|
||||
state.dataByteBuffer.putLong(dataIndex++ * MachineState.VALUE_SIZE, state.b1);
|
||||
state.dataByteBuffer.putLong(dataIndex++ * MachineState.VALUE_SIZE, state.b2);
|
||||
@ -283,10 +281,9 @@ public enum FunctionCode {
|
||||
@Override
|
||||
protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException {
|
||||
// Validate data offset in arg1
|
||||
if (functionData.value1 < 0L || functionData.value1 > Integer.MAX_VALUE || functionData.value1 >= state.numDataPages - 3)
|
||||
throw new ExecutionException(this.name() + " data start address out of bounds");
|
||||
checkDataAddress(state, functionData.value1, 4);
|
||||
|
||||
int dataIndex = (int) (functionData.value1 & 0x7fffffffL);
|
||||
int dataIndex = (int) (functionData.value1 & Integer.MAX_VALUE);
|
||||
|
||||
state.a1 = state.dataByteBuffer.getLong(dataIndex++ * MachineState.VALUE_SIZE);
|
||||
state.a2 = state.dataByteBuffer.getLong(dataIndex++ * MachineState.VALUE_SIZE);
|
||||
@ -302,10 +299,9 @@ public enum FunctionCode {
|
||||
@Override
|
||||
protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException {
|
||||
// Validate data offset in arg1
|
||||
if (functionData.value1 < 0L || functionData.value1 > Integer.MAX_VALUE || functionData.value1 >= state.numDataPages - 3)
|
||||
throw new ExecutionException(this.name() + " data start address out of bounds");
|
||||
checkDataAddress(state, functionData.value1, 4);
|
||||
|
||||
int dataIndex = (int) (functionData.value1 & 0x7fffffffL);
|
||||
int dataIndex = (int) (functionData.value1 & Integer.MAX_VALUE);
|
||||
|
||||
state.b1 = state.dataByteBuffer.getLong(dataIndex++ * MachineState.VALUE_SIZE);
|
||||
state.b2 = state.dataByteBuffer.getLong(dataIndex++ * MachineState.VALUE_SIZE);
|
||||
@ -1081,15 +1077,14 @@ public enum FunctionCode {
|
||||
|
||||
protected byte[] getHashData(FunctionData functionData, MachineState state) throws ExecutionException {
|
||||
// Validate data offset in arg1
|
||||
if (functionData.value1 < 0L || functionData.value1 > Integer.MAX_VALUE || functionData.value1 >= state.numDataPages)
|
||||
throw new ExecutionException(this.name() + " data start address out of bounds");
|
||||
checkDataAddress(state, functionData.value1, 1);
|
||||
|
||||
// Validate data length in arg2
|
||||
if (functionData.value2 < 0L || functionData.value2 > Integer.MAX_VALUE || functionData.value1 + byteLengthToDataLength(functionData.value2) > state.numDataPages)
|
||||
if (functionData.value2 < 0L || functionData.value2 > state.numDataPages || functionData.value1 + byteLengthToDataLength(functionData.value2) > state.numDataPages)
|
||||
throw new ExecutionException(this.name() + " data length invalid");
|
||||
|
||||
final int dataStart = (int) (functionData.value1 & 0x7fffffffL);
|
||||
final int dataLength = (int) (functionData.value2 & 0x7fffffffL);
|
||||
final int dataStart = (int) (functionData.value1 & Integer.MAX_VALUE);
|
||||
final int dataLength = (int) (functionData.value2 & Integer.MAX_VALUE);
|
||||
|
||||
byte[] message = new byte[dataLength];
|
||||
|
||||
@ -1104,7 +1099,15 @@ public enum FunctionCode {
|
||||
|
||||
/** Returns the number of data-page values to contain specific length of bytes. */
|
||||
protected int byteLengthToDataLength(long byteLength) {
|
||||
return (MachineState.VALUE_SIZE - 1 + (int) (byteLength & 0x7fffffffL)) / MachineState.VALUE_SIZE;
|
||||
return (MachineState.VALUE_SIZE - 1 + (int) (byteLength & Integer.MAX_VALUE)) / MachineState.VALUE_SIZE;
|
||||
}
|
||||
|
||||
/** Check that data segment address (+ subsequent locations) are within data segment bounds. */
|
||||
protected void checkDataAddress(MachineState state, long address, int count) throws ExecutionException {
|
||||
final int maxAddress = state.numDataPages - count;
|
||||
|
||||
if (address < 0L || address > maxAddress)
|
||||
throw new ExecutionException(String.format("%s data address %d out of bounds: 0 to %d", this.name(), address, maxAddress));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ public enum OpCode {
|
||||
*/
|
||||
NOP(0x7f) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) {
|
||||
protected void executeWithParams(MachineState state, Object... args) {
|
||||
// Do nothing
|
||||
}
|
||||
},
|
||||
@ -50,7 +50,7 @@ public enum OpCode {
|
||||
*/
|
||||
SET_VAL(0x01, OpCodeParam.DEST_ADDR, OpCodeParam.VALUE) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
int address = (int) args[0];
|
||||
long value = (long) args[1];
|
||||
|
||||
@ -64,7 +64,7 @@ public enum OpCode {
|
||||
*/
|
||||
SET_DAT(0x02, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
int address1 = (int) args[0];
|
||||
int address2 = (int) args[1];
|
||||
|
||||
@ -79,7 +79,7 @@ public enum OpCode {
|
||||
*/
|
||||
CLR_DAT(0x03, OpCodeParam.DEST_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
int address = (int) args[0];
|
||||
|
||||
state.dataByteBuffer.putLong(address, 0L);
|
||||
@ -92,7 +92,7 @@ public enum OpCode {
|
||||
*/
|
||||
INC_DAT(0x04, OpCodeParam.DEST_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
int address = (int) args[0];
|
||||
|
||||
long value = state.dataByteBuffer.getLong(address);
|
||||
@ -106,7 +106,7 @@ public enum OpCode {
|
||||
*/
|
||||
DEC_DAT(0x05, OpCodeParam.DEST_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
int address = (int) args[0];
|
||||
|
||||
long value = state.dataByteBuffer.getLong(address);
|
||||
@ -120,7 +120,7 @@ public enum OpCode {
|
||||
*/
|
||||
ADD_DAT(0x06, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
executeDataOperation(state, (a, b) -> a + b, args);
|
||||
}
|
||||
},
|
||||
@ -131,7 +131,7 @@ public enum OpCode {
|
||||
*/
|
||||
SUB_DAT(0x07, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
executeDataOperation(state, (a, b) -> a - b, args);
|
||||
}
|
||||
},
|
||||
@ -142,7 +142,7 @@ public enum OpCode {
|
||||
*/
|
||||
MUL_DAT(0x08, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
executeDataOperation(state, (a, b) -> a * b, args);
|
||||
}
|
||||
},
|
||||
@ -154,7 +154,7 @@ public enum OpCode {
|
||||
*/
|
||||
DIV_DAT(0x09, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
try {
|
||||
executeDataOperation(state, (a, b) -> a / b, args);
|
||||
} catch (ArithmeticException e) {
|
||||
@ -169,7 +169,7 @@ public enum OpCode {
|
||||
*/
|
||||
BOR_DAT(0x0a, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
executeDataOperation(state, (a, b) -> a | b, args);
|
||||
}
|
||||
},
|
||||
@ -180,7 +180,7 @@ public enum OpCode {
|
||||
*/
|
||||
AND_DAT(0x0b, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
executeDataOperation(state, (a, b) -> a & b, args);
|
||||
}
|
||||
},
|
||||
@ -191,7 +191,7 @@ public enum OpCode {
|
||||
*/
|
||||
XOR_DAT(0x0c, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
executeDataOperation(state, (a, b) -> a ^ b, args);
|
||||
}
|
||||
},
|
||||
@ -202,7 +202,7 @@ public enum OpCode {
|
||||
*/
|
||||
NOT_DAT(0x0d, OpCodeParam.DEST_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
int address = (int) args[0];
|
||||
|
||||
long value = state.dataByteBuffer.getLong(address);
|
||||
@ -216,7 +216,7 @@ public enum OpCode {
|
||||
*/
|
||||
SET_IND(0x0e, OpCodeParam.DEST_ADDR, OpCodeParam.INDIRECT_SRC_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
int address1 = (int) args[0];
|
||||
int address2 = (int) args[1];
|
||||
|
||||
@ -236,7 +236,7 @@ public enum OpCode {
|
||||
*/
|
||||
SET_IDX(0x0f, OpCodeParam.DEST_ADDR, OpCodeParam.INDIRECT_SRC_ADDR_WITH_INDEX, OpCodeParam.INDEX) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
int address1 = (int) args[0];
|
||||
int address2 = (int) args[1];
|
||||
int address3 = (int) args[2];
|
||||
@ -261,7 +261,7 @@ public enum OpCode {
|
||||
*/
|
||||
PSH_DAT(0x10, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
int address = (int) args[0];
|
||||
|
||||
long value = state.dataByteBuffer.getLong(address);
|
||||
@ -284,7 +284,7 @@ public enum OpCode {
|
||||
*/
|
||||
POP_DAT(0x11, OpCodeParam.DEST_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
int address = (int) args[0];
|
||||
|
||||
try {
|
||||
@ -308,7 +308,7 @@ public enum OpCode {
|
||||
*/
|
||||
JMP_SUB(0x12, OpCodeParam.CODE_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
int address = (int) args[0];
|
||||
|
||||
try {
|
||||
@ -331,7 +331,7 @@ public enum OpCode {
|
||||
*/
|
||||
RET_SUB(0x13) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
try {
|
||||
int returnAddress = state.callStackByteBuffer.getInt();
|
||||
|
||||
@ -351,7 +351,7 @@ public enum OpCode {
|
||||
*/
|
||||
IND_DAT(0x14, OpCodeParam.INDIRECT_DEST_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
int address1 = (int) args[0];
|
||||
int address2 = (int) args[1];
|
||||
|
||||
@ -371,7 +371,7 @@ public enum OpCode {
|
||||
*/
|
||||
IDX_DAT(0x15, OpCodeParam.INDIRECT_DEST_ADDR_WITH_INDEX, OpCodeParam.INDEX, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
int address1 = (int) args[0];
|
||||
int address2 = (int) args[1];
|
||||
int address3 = (int) args[2];
|
||||
@ -395,7 +395,7 @@ public enum OpCode {
|
||||
*/
|
||||
MOD_DAT(0x16, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
try {
|
||||
executeDataOperation(state, (a, b) -> a % b, args);
|
||||
} catch (ArithmeticException e) {
|
||||
@ -412,7 +412,7 @@ public enum OpCode {
|
||||
private static final long MAX_SHIFT = MachineState.VALUE_SIZE * 8L;
|
||||
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
// If 2nd arg is more than value size (in bits) then return 0 to simulate all bits being shifted out of existence
|
||||
executeDataOperation(state, (a, b) -> b >= MAX_SHIFT ? 0 : a << b, args);
|
||||
}
|
||||
@ -427,7 +427,7 @@ public enum OpCode {
|
||||
private static final long MAX_SHIFT = MachineState.VALUE_SIZE * 8L;
|
||||
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
// If 2nd arg is more than value size (in bits) then return 0 to simulate all bits being shifted out of existence
|
||||
executeDataOperation(state, (a, b) -> b >= MAX_SHIFT ? 0 : a >>> b, args);
|
||||
}
|
||||
@ -439,7 +439,7 @@ public enum OpCode {
|
||||
*/
|
||||
JMP_ADR(0x1a, OpCodeParam.CODE_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
int address = (int) args[0];
|
||||
|
||||
state.codeByteBuffer.position(address);
|
||||
@ -453,7 +453,7 @@ public enum OpCode {
|
||||
*/
|
||||
BZR_DAT(0x1b, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
int address = (int) args[0];
|
||||
byte offset = (byte) args[1];
|
||||
|
||||
@ -476,7 +476,7 @@ public enum OpCode {
|
||||
*/
|
||||
BNZ_DAT(0x1e, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
int address = (int) args[0];
|
||||
byte offset = (byte) args[1];
|
||||
|
||||
@ -499,7 +499,7 @@ public enum OpCode {
|
||||
*/
|
||||
BGT_DAT(0x1f, OpCodeParam.SRC_ADDR, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
executeBranchConditional(state, (a, b) -> a > b, args);
|
||||
}
|
||||
},
|
||||
@ -511,7 +511,7 @@ public enum OpCode {
|
||||
*/
|
||||
BLT_DAT(0x20, OpCodeParam.SRC_ADDR, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
executeBranchConditional(state, (a, b) -> a < b, args);
|
||||
}
|
||||
},
|
||||
@ -523,7 +523,7 @@ public enum OpCode {
|
||||
*/
|
||||
BGE_DAT(0x21, OpCodeParam.SRC_ADDR, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
executeBranchConditional(state, (a, b) -> a >= b, args);
|
||||
}
|
||||
},
|
||||
@ -535,7 +535,7 @@ public enum OpCode {
|
||||
*/
|
||||
BLE_DAT(0x22, OpCodeParam.SRC_ADDR, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
executeBranchConditional(state, (a, b) -> a <= b, args);
|
||||
}
|
||||
},
|
||||
@ -547,7 +547,7 @@ public enum OpCode {
|
||||
*/
|
||||
BEQ_DAT(0x23, OpCodeParam.SRC_ADDR, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
executeBranchConditional(state, (a, b) -> a == b, args);
|
||||
}
|
||||
},
|
||||
@ -559,7 +559,7 @@ public enum OpCode {
|
||||
*/
|
||||
BNE_DAT(0x24, OpCodeParam.SRC_ADDR, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
executeBranchConditional(state, (a, b) -> a != b, args);
|
||||
}
|
||||
},
|
||||
@ -571,7 +571,7 @@ public enum OpCode {
|
||||
*/
|
||||
SLP_DAT(0x25, OpCodeParam.BLOCK_HEIGHT) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
int address = (int) args[0];
|
||||
|
||||
long value = state.codeByteBuffer.getLong(address);
|
||||
@ -587,7 +587,7 @@ public enum OpCode {
|
||||
*/
|
||||
FIZ_DAT(0x26, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
int address = (int) args[0];
|
||||
|
||||
long value = state.dataByteBuffer.getLong(address);
|
||||
@ -603,7 +603,7 @@ public enum OpCode {
|
||||
*/
|
||||
STZ_DAT(0x27, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
int address = (int) args[0];
|
||||
|
||||
long value = state.dataByteBuffer.getLong(address);
|
||||
@ -621,7 +621,7 @@ public enum OpCode {
|
||||
*/
|
||||
FIN_IMD(0x28) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
state.setIsFinished(true);
|
||||
}
|
||||
},
|
||||
@ -632,7 +632,7 @@ public enum OpCode {
|
||||
*/
|
||||
STP_IMD(0x29) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) {
|
||||
protected void executeWithParams(MachineState state, Object... args) {
|
||||
state.setIsStopped(true);
|
||||
}
|
||||
},
|
||||
@ -643,7 +643,7 @@ public enum OpCode {
|
||||
*/
|
||||
SLP_IMD(0x2a) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) {
|
||||
protected void executeWithParams(MachineState state, Object... args) {
|
||||
state.setSleepUntilHeight(state.getCurrentBlockHeight() + 1);
|
||||
state.setIsSleeping(true);
|
||||
}
|
||||
@ -655,7 +655,7 @@ public enum OpCode {
|
||||
*/
|
||||
ERR_ADR(0x2b, OpCodeParam.CODE_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
int address = (int) args[0];
|
||||
|
||||
state.setOnErrorAddress(address);
|
||||
@ -669,7 +669,7 @@ public enum OpCode {
|
||||
*/
|
||||
SET_PCS(0x30) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) {
|
||||
protected void executeWithParams(MachineState state, Object... args) {
|
||||
state.setOnStopAddress(state.codeByteBuffer.position());
|
||||
}
|
||||
},
|
||||
@ -680,7 +680,7 @@ public enum OpCode {
|
||||
*/
|
||||
EXT_FUN(0x32, OpCodeParam.FUNC) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
short rawFunctionCode = (short) args[0];
|
||||
|
||||
FunctionCode functionCode = FunctionCode.valueOf(rawFunctionCode);
|
||||
@ -702,7 +702,7 @@ public enum OpCode {
|
||||
*/
|
||||
EXT_FUN_DAT(0x33, OpCodeParam.FUNC, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
short rawFunctionCode = (short) args[0];
|
||||
int address = (int) args[1];
|
||||
|
||||
@ -727,7 +727,7 @@ public enum OpCode {
|
||||
*/
|
||||
EXT_FUN_DAT_2(0x34, OpCodeParam.FUNC, OpCodeParam.SRC_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
short rawFunctionCode = (short) args[0];
|
||||
int address1 = (int) args[1];
|
||||
int address2 = (int) args[2];
|
||||
@ -754,7 +754,7 @@ public enum OpCode {
|
||||
*/
|
||||
EXT_FUN_RET(0x35, OpCodeParam.FUNC, OpCodeParam.DEST_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
short rawFunctionCode = (short) args[0];
|
||||
int address = (int) args[1];
|
||||
|
||||
@ -782,7 +782,7 @@ public enum OpCode {
|
||||
*/
|
||||
EXT_FUN_RET_DAT(0x36, OpCodeParam.FUNC, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
short rawFunctionCode = (short) args[0];
|
||||
int address1 = (int) args[1];
|
||||
int address2 = (int) args[2];
|
||||
@ -813,7 +813,7 @@ public enum OpCode {
|
||||
*/
|
||||
EXT_FUN_RET_DAT_2(0x37, OpCodeParam.FUNC, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
public void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
short rawFunctionCode = (short) args[0];
|
||||
int address1 = (int) args[1];
|
||||
int address2 = (int) args[2];
|
||||
@ -842,7 +842,7 @@ public enum OpCode {
|
||||
};
|
||||
|
||||
public final byte value;
|
||||
public final OpCodeParam[] params;
|
||||
private final OpCodeParam[] params;
|
||||
|
||||
// Create a map of opcode values to OpCode
|
||||
private static final Map<Byte, OpCode> map = Arrays.stream(OpCode.values()).collect(Collectors.toMap(opcode -> opcode.value, opcode -> opcode));
|
||||
@ -873,7 +873,7 @@ public enum OpCode {
|
||||
*
|
||||
* @throws ExecutionException
|
||||
*/
|
||||
public abstract void executeWithParams(MachineState state, Object... args) throws ExecutionException;
|
||||
protected abstract void executeWithParams(MachineState state, Object... args) throws ExecutionException;
|
||||
|
||||
public void execute(MachineState state) throws ExecutionException {
|
||||
List<Object> args = new ArrayList<>();
|
||||
@ -914,7 +914,7 @@ public enum OpCode {
|
||||
* - typically a lambda operating on two <tt>long</tt> params, e.g. <tt>(a, b) -> a + b</tt>
|
||||
* @throws ExecutionException
|
||||
*/
|
||||
private static void executeDataOperation(MachineState state, TwoValueOperator operator, Object... args) throws ExecutionException {
|
||||
protected void executeDataOperation(MachineState state, TwoValueOperator operator, Object... args) throws ExecutionException {
|
||||
int address1 = (int) args[0];
|
||||
int address2 = (int) args[1];
|
||||
|
||||
@ -936,13 +936,16 @@ public enum OpCode {
|
||||
* - typically a lambda comparing two <tt>long</tt> params, e.g. <tt>(a, b) -> a == b</tt>
|
||||
* @throws ExecutionException
|
||||
*/
|
||||
private static void executeBranchConditional(MachineState state, TwoValueComparator comparator, Object... args) throws ExecutionException {
|
||||
protected void executeBranchConditional(MachineState state, TwoValueComparator comparator, Object... args) throws ExecutionException {
|
||||
int address1 = (int) args[0];
|
||||
int address2 = (int) args[1];
|
||||
byte offset = (byte) args[2];
|
||||
|
||||
int branchTarget = state.getProgramCounter() + offset;
|
||||
|
||||
if (branchTarget < 0 || branchTarget >= state.codeByteBuffer.limit())
|
||||
throw new InvalidAddressException("branch target out of bounds");
|
||||
|
||||
long value1 = state.dataByteBuffer.getLong(address1);
|
||||
long value2 = state.dataByteBuffer.getLong(address2);
|
||||
|
||||
|
@ -94,10 +94,12 @@ public class Utils {
|
||||
*/
|
||||
public static byte getCodeOffset(ByteBuffer codeByteBuffer) throws CodeSegmentException, InvalidAddressException {
|
||||
try {
|
||||
byte offset = codeByteBuffer.get();
|
||||
final byte offset = codeByteBuffer.get();
|
||||
final int target = codeByteBuffer.position() + offset;
|
||||
|
||||
if (codeByteBuffer.position() + offset < 0 || codeByteBuffer.position() + offset >= codeByteBuffer.limit())
|
||||
throw new InvalidAddressException("Code offset out of bounds");
|
||||
if (target < 0 || target >= codeByteBuffer.limit() - 1)
|
||||
throw new InvalidAddressException(String.format("Code target PC+%02x=[%04x] out of bounds: 0x0000 to 0x%04x",
|
||||
codeByteBuffer.position() - 1, offset, target, codeByteBuffer.limit() - 1 -1));
|
||||
|
||||
return offset;
|
||||
} catch (BufferUnderflowException e) {
|
||||
|
@ -1,3 +1,5 @@
|
||||
package org.ciyam.at;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
@ -9,38 +11,15 @@ import org.ciyam.at.FunctionCode;
|
||||
import org.ciyam.at.MachineState;
|
||||
import org.ciyam.at.OpCode;
|
||||
import org.ciyam.at.Timestamp;
|
||||
import org.ciyam.at.test.ExecutableTest;
|
||||
import org.ciyam.at.test.TestAPI;
|
||||
import org.ciyam.at.test.TestAPI.TestAccount;
|
||||
import org.ciyam.at.test.TestAPI.TestBlock;
|
||||
import org.ciyam.at.test.TestAPI.TestTransaction;
|
||||
import org.junit.Test;
|
||||
|
||||
import common.ExecutableTest;
|
||||
import common.TestAPI;
|
||||
import common.TestAPI.TestAccount;
|
||||
import common.TestAPI.TestBlock;
|
||||
import common.TestAPI.TestTransaction;
|
||||
|
||||
public class BlockchainFunctionCodeTests extends ExecutableTest {
|
||||
|
||||
/**
|
||||
* GET_BLOCK_TIMESTAMP
|
||||
* GET_CREATION_TIMESTAMP
|
||||
* GET_PREVIOUS_BLOCK_TIMESTAMP
|
||||
* PUT_PREVIOUS_BLOCK_HASH_INTO_A
|
||||
* PUT_TX_AFTER_TIMESTAMP_INTO_A
|
||||
* GET_TYPE_FROM_TX_IN_A
|
||||
* GET_AMOUNT_FROM_TX_IN_A
|
||||
* GET_TIMESTAMP_FROM_TX_IN_A
|
||||
* GENERATE_RANDOM_USING_TX_IN_A
|
||||
* PUT_MESSAGE_FROM_TX_IN_A_INTO_B
|
||||
* PUT_ADDRESS_FROM_TX_IN_A_INTO_B
|
||||
* PUT_CREATOR_INTO_B
|
||||
* GET_CURRENT_BALANCE
|
||||
* GET_PREVIOUS_BALANCE
|
||||
* PAY_TO_ADDRESS_IN_B
|
||||
* PAY_ALL_TO_ADDRESS_IN_B
|
||||
* PAY_PREVIOUS_TO_ADDRESS_IN_B
|
||||
* MESSAGE_A_TO_ADDRESS_IN_B
|
||||
* ADD_MINUTES_TO_TIMESTAMP
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void testGetBlockTimestamp() throws ExecutionException {
|
||||
// Grab block 'timestamp' and save into address 0
|
@ -1,11 +1,12 @@
|
||||
package org.ciyam.at;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.ciyam.at.ExecutionException;
|
||||
import org.ciyam.at.OpCode;
|
||||
import org.ciyam.at.test.ExecutableTest;
|
||||
import org.junit.Test;
|
||||
|
||||
import common.ExecutableTest;
|
||||
|
||||
public class BranchingOpCodeTests extends ExecutableTest {
|
||||
|
||||
@Test
|
||||
@ -35,6 +36,36 @@ public class BranchingOpCodeTests extends ExecutableTest {
|
||||
assertEquals("Data does not match", 2L, getData(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOutOfBoundsForwardsBranch() throws ExecutionException {
|
||||
int forwardAddr = codeByteBuffer.limit() - 60; // enough room for post-jump code
|
||||
|
||||
codeByteBuffer.put(OpCode.JMP_ADR.value).putInt(forwardAddr);
|
||||
|
||||
codeByteBuffer.position(forwardAddr);
|
||||
|
||||
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(0).putLong(0L);
|
||||
codeByteBuffer.put(OpCode.BZR_DAT.value).putInt(0).put((byte) 80); // way after end
|
||||
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(1).putLong(1L);
|
||||
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||
|
||||
execute(true);
|
||||
|
||||
assertTrue(state.getHadFatalError());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOutOfBoundsBackwardsBranch() throws ExecutionException {
|
||||
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(0).putLong(0L);
|
||||
codeByteBuffer.put(OpCode.BZR_DAT.value).putInt(0).put((byte) -80); // way before start
|
||||
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(1).putLong(1L);
|
||||
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||
|
||||
execute(true);
|
||||
|
||||
assertTrue(state.getHadFatalError());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBZR_DATtrue() throws ExecutionException {
|
||||
int targetAddr = 0x21;
|
@ -1,12 +1,13 @@
|
||||
package org.ciyam.at;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.ciyam.at.ExecutionException;
|
||||
import org.ciyam.at.MachineState;
|
||||
import org.ciyam.at.OpCode;
|
||||
import org.ciyam.at.test.ExecutableTest;
|
||||
import org.junit.Test;
|
||||
|
||||
import common.ExecutableTest;
|
||||
|
||||
public class CallStackOpCodeTests extends ExecutableTest {
|
||||
|
||||
@Test
|
@ -1,11 +1,12 @@
|
||||
package org.ciyam.at;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.ciyam.at.ExecutionException;
|
||||
import org.ciyam.at.OpCode;
|
||||
import org.ciyam.at.test.ExecutableTest;
|
||||
import org.junit.Test;
|
||||
|
||||
import common.ExecutableTest;
|
||||
|
||||
public class DataOpCodeTests extends ExecutableTest {
|
||||
|
||||
@Test
|
@ -1,16 +1,19 @@
|
||||
import static common.TestUtils.hexToBytes;
|
||||
package org.ciyam.at;
|
||||
|
||||
import static org.ciyam.at.test.TestUtils.hexToBytes;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Random;
|
||||
|
||||
import org.ciyam.at.ExecutionException;
|
||||
import org.ciyam.at.FunctionCode;
|
||||
import org.ciyam.at.MachineState;
|
||||
import org.ciyam.at.OpCode;
|
||||
import org.ciyam.at.test.ExecutableTest;
|
||||
import org.ciyam.at.test.TestUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import common.ExecutableTest;
|
||||
import common.TestUtils;
|
||||
|
||||
public class DisassemblyTests extends ExecutableTest {
|
||||
|
||||
private static final String message = "The quick, brown fox jumped over the lazy dog.";
|
||||
@ -55,4 +58,28 @@ public class DisassemblyTests extends ExecutableTest {
|
||||
System.out.println(state.disassemble());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFuzzyDisassembly() {
|
||||
Random random = new Random();
|
||||
|
||||
byte[] randomCode = new byte[200];
|
||||
random.nextBytes(randomCode);
|
||||
codeByteBuffer.put(randomCode);
|
||||
|
||||
byte[] headerBytes = TestUtils.HEADER_BYTES;
|
||||
byte[] codeBytes = codeByteBuffer.array();
|
||||
byte[] dataBytes = dataByteBuffer.array();
|
||||
|
||||
state = new MachineState(api, logger, headerBytes, codeBytes, dataBytes);
|
||||
|
||||
try {
|
||||
System.out.println(state.disassemble());
|
||||
} catch (ExecutionException e) {
|
||||
// we expect this to fail
|
||||
return;
|
||||
}
|
||||
|
||||
fail("Random code should cause disassembly failure");
|
||||
}
|
||||
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
package org.ciyam.at;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
@ -7,10 +9,9 @@ import org.ciyam.at.FunctionCode;
|
||||
import org.ciyam.at.MachineState;
|
||||
import org.ciyam.at.OpCode;
|
||||
import org.ciyam.at.Timestamp;
|
||||
import org.ciyam.at.test.ExecutableTest;
|
||||
import org.junit.Test;
|
||||
|
||||
import common.ExecutableTest;
|
||||
|
||||
public class FunctionCodeTests extends ExecutableTest {
|
||||
|
||||
private static final byte[] TEST_BYTES = "This string is exactly 32 bytes!".getBytes();
|
@ -1,4 +1,6 @@
|
||||
import static common.TestUtils.hexToBytes;
|
||||
package org.ciyam.at;
|
||||
|
||||
import static org.ciyam.at.test.TestUtils.hexToBytes;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@ -6,10 +8,9 @@ import java.nio.charset.StandardCharsets;
|
||||
import org.ciyam.at.ExecutionException;
|
||||
import org.ciyam.at.FunctionCode;
|
||||
import org.ciyam.at.OpCode;
|
||||
import org.ciyam.at.test.ExecutableTest;
|
||||
import org.junit.Test;
|
||||
|
||||
import common.ExecutableTest;
|
||||
|
||||
public class HashingFunctionCodeTests extends ExecutableTest {
|
||||
|
||||
private static final String message = "The quick, brown fox jumped over the lazy dog.";
|
@ -1,15 +1,16 @@
|
||||
package org.ciyam.at;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.ciyam.at.ExecutionException;
|
||||
import org.ciyam.at.FunctionCode;
|
||||
import org.ciyam.at.MachineState;
|
||||
import org.ciyam.at.OpCode;
|
||||
import org.ciyam.at.test.ExecutableTest;
|
||||
import org.ciyam.at.test.TestAPI;
|
||||
import org.ciyam.at.test.TestUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import common.ExecutableTest;
|
||||
import common.TestAPI;
|
||||
import common.TestUtils;
|
||||
|
||||
public class MiscTests extends ExecutableTest {
|
||||
|
||||
@Test
|
@ -1,12 +1,13 @@
|
||||
import static common.TestUtils.hexToBytes;
|
||||
package org.ciyam.at;
|
||||
|
||||
import static org.ciyam.at.test.TestUtils.hexToBytes;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.ciyam.at.ExecutionException;
|
||||
import org.ciyam.at.OpCode;
|
||||
import org.ciyam.at.test.ExecutableTest;
|
||||
import org.junit.Test;
|
||||
|
||||
import common.ExecutableTest;
|
||||
|
||||
public class OpCodeTests extends ExecutableTest {
|
||||
|
||||
@Test
|
@ -1,4 +1,6 @@
|
||||
import static common.TestUtils.hexToBytes;
|
||||
package org.ciyam.at;
|
||||
|
||||
import static org.ciyam.at.test.TestUtils.hexToBytes;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
@ -7,11 +9,10 @@ import org.ciyam.at.ExecutionException;
|
||||
import org.ciyam.at.FunctionCode;
|
||||
import org.ciyam.at.MachineState;
|
||||
import org.ciyam.at.OpCode;
|
||||
import org.ciyam.at.test.ExecutableTest;
|
||||
import org.ciyam.at.test.TestUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import common.ExecutableTest;
|
||||
import common.TestUtils;
|
||||
|
||||
public class SerializationTests extends ExecutableTest {
|
||||
|
||||
private byte[] simulate() {
|
@ -1,4 +1,6 @@
|
||||
import static common.TestUtils.hexToBytes;
|
||||
package org.ciyam.at;
|
||||
|
||||
import static org.ciyam.at.test.TestUtils.hexToBytes;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Arrays;
|
@ -1,12 +1,13 @@
|
||||
package org.ciyam.at;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.ciyam.at.ExecutionException;
|
||||
import org.ciyam.at.MachineState;
|
||||
import org.ciyam.at.OpCode;
|
||||
import org.ciyam.at.test.ExecutableTest;
|
||||
import org.junit.Test;
|
||||
|
||||
import common.ExecutableTest;
|
||||
|
||||
public class UserStackOpCodeTests extends ExecutableTest {
|
||||
|
||||
@Test
|
@ -1,4 +1,4 @@
|
||||
package common;
|
||||
package org.ciyam.at.test;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.Security;
|
@ -1,4 +1,4 @@
|
||||
package common;
|
||||
package org.ciyam.at.test;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
@ -1,4 +1,4 @@
|
||||
package common;
|
||||
package org.ciyam.at.test;
|
||||
|
||||
import org.ciyam.at.LoggerInterface;
|
||||
|
@ -1,4 +1,4 @@
|
||||
package common;
|
||||
package org.ciyam.at.test;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
Loading…
Reference in New Issue
Block a user