+ SDIV, SMOD, MOD, SLT, SGT ops implemented and unittested
+ Gas calculation for memory & storage
This commit is contained in:
romanman 2014-06-05 12:36:36 +03:00
parent 517bb2cec1
commit b9d551d2fa
4 changed files with 475 additions and 49 deletions

View File

@ -41,6 +41,11 @@ public class DataWord {
return new BigInteger(1, data);
}
public BigInteger sValue(){
return new BigInteger(data);
}
public boolean isZero(){
byte result = 0;
@ -50,6 +55,17 @@ public class DataWord {
return result == 0;
}
// only in case of signed operation
// when the number is explicit defined
// as negative
public boolean isNegative(){
int result = data[0] & 0x80;
return result == 0x80;
}
public DataWord and(DataWord w2){
for (int i = 0; i < this.data.length; ++i){
@ -144,6 +160,26 @@ public class DataWord {
this.data = data.array();
}
// todo: improve with no BigInteger
public void sDiv(DataWord word){
if (word.isZero()){
this.and(ZERO);
return;
}
BigInteger result = sValue().divide( word.sValue() );
byte[] bytes = result.toByteArray();
ByteBuffer data = ByteBuffer.allocate(32);
if (result.compareTo(BigInteger.ZERO) == -1)
Arrays.fill(data.array(), (byte) 0xFF);
System.arraycopy(bytes, 0, data.array(), 32 - bytes.length, bytes.length);
this.data = data.array();
}
// todo: improve with no BigInteger
public void sub(DataWord word){
@ -166,6 +202,40 @@ public class DataWord {
this.data = data.array();
}
// todo: improve with no BigInteger
public void mod(DataWord word){
if (word.isZero()){
this.and(ZERO);
return;
}
BigInteger result = value().mod(word.value());
byte[] bytes = result.toByteArray();
ByteBuffer data = ByteBuffer.allocate(32);
System.arraycopy(bytes, 0, data.array(), 32 - bytes.length, bytes.length);
this.data = data.array();
}
// todo: improve with no BigInteger
public void sMod(DataWord word){
if (word.isZero() || word.isNegative()){
this.and(ZERO);
return;
}
BigInteger result = sValue().mod( word.sValue());
byte[] bytes = result.toByteArray();
ByteBuffer data = ByteBuffer.allocate(32);
if (result.compareTo(BigInteger.ZERO) == -1)
Arrays.fill(data.array(), (byte) 0xFF);
System.arraycopy(bytes, 0, data.array(), 32 - bytes.length, bytes.length);
this.data = data.array();
}
public String toString(){
return Hex.toHexString(data);

View File

@ -190,7 +190,7 @@ public class Program {
public void spendGas(int gasValue){
// todo: check it against avail gas
// todo: out of gas will revert the changes
// todo: out of gas will revert the changes [YP 5, 6 ]
spendGas += gasValue;
}
@ -257,6 +257,9 @@ public class Program {
public void fullTrace(){
// todo: add gas to full trace calc
if (logger.isDebugEnabled()){
StringBuilder stackData = new StringBuilder();

View File

@ -30,6 +30,7 @@ public class VM {
byte op = program.getCurrentOp();
logger.debug("Op: {}" ,OpCode.code(op).name());
int oldMemSize = program.getMemSize();
switch (OpCode.code(op)) {
case SHA3:
@ -110,12 +111,33 @@ public class VM {
program.step();
}
break;
case SDIV:
break;
case MOD:
break;
case SMOD:
break;
case SDIV:{
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
word1.sDiv(word2);
program.stackPush(word1);
program.step();
}
break;
case MOD:{
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
word1.mod(word2);
program.stackPush(word1);
program.step();
}
break;
case SMOD:{
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
word1.sMod(word2);
program.stackPush(word1);
program.step();
}
break;
case EXP:{
DataWord word1 = program.stackPop();
@ -133,6 +155,8 @@ public class VM {
}
break;
case LT:{
// todo: can be improved by not using BigInteger
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
if (word1.value().compareTo(word2.value()) == -1){
@ -145,11 +169,37 @@ public class VM {
program.step();
}
break;
case SLT:
break;
case SGT:
break;
case SLT:{
// todo: can be improved by not using BigInteger
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
if (word1.sValue().compareTo(word2.sValue()) == -1){
word1.and(DataWord.ZERO);
word1.getData()[31] = 1;
} else {
word1.and(DataWord.ZERO);
}
program.stackPush(word1);
program.step();
}break;
case SGT:{
// todo: can be improved by not using BigInteger
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
if (word1.sValue().compareTo(word2.sValue()) == 1){
word1.and(DataWord.ZERO);
word1.getData()[31] = 1;
} else {
word1.and(DataWord.ZERO);
}
program.stackPush(word1);
program.step();
}break;
case GT:{
// todo: can be improved by not using BigInteger
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
if (word1.value().compareTo(word2.value()) == 1){
@ -418,7 +468,16 @@ public class VM {
DataWord addr = program.stackPop();
DataWord value = program.stackPop();
// for gas calculations [YP 9.2]
DataWord oldValue = program.storageLoad(addr);
program.storageSave(addr, value);
if (oldValue == null && !value.isZero()){
program.spendGas(GasLedger.G_SSTORE * 2);
} else if (oldValue != null && value.isZero()){
program.spendGas(GasLedger.G_SSTORE * 0);
} else
program.spendGas(GasLedger.G_SSTORE);
program.step();
}
break;
@ -456,38 +515,10 @@ public class VM {
case GAS:
break;
case PUSH1:
case PUSH2:
case PUSH3:
case PUSH4:
case PUSH5:
case PUSH6:
case PUSH7:
case PUSH8:
case PUSH9:
case PUSH10:
case PUSH11:
case PUSH12:
case PUSH13:
case PUSH14:
case PUSH15:
case PUSH16:
case PUSH17:
case PUSH18:
case PUSH19:
case PUSH20:
case PUSH21:
case PUSH22:
case PUSH23:
case PUSH24:
case PUSH25:
case PUSH26:
case PUSH27:
case PUSH28:
case PUSH29:
case PUSH30:
case PUSH31:
case PUSH32:{
case PUSH1: case PUSH2: case PUSH3: case PUSH4: case PUSH5: case PUSH6: case PUSH7: case PUSH8:
case PUSH9: case PUSH10: case PUSH11: case PUSH12: case PUSH13: case PUSH14: case PUSH15: case PUSH16:
case PUSH17: case PUSH18: case PUSH19: case PUSH20: case PUSH21: case PUSH22: case PUSH23: case PUSH24:
case PUSH25: case PUSH26: case PUSH27: case PUSH28: case PUSH29: case PUSH30: case PUSH31: case PUSH32:{
program.step();
int nPush = op - PUSH1.val() + 1;
@ -521,7 +552,9 @@ public class VM {
default:{
}
// memory gas calc
int newMemSize = program.getMemSize();
program.spendGas(GasLedger.G_MEMORY * (newMemSize - oldMemSize) /32);
}
program.fullTrace();
} catch (RuntimeException e) {

View File

@ -15,7 +15,6 @@ import static org.junit.Assert.fail;
public class VMTest {
@Test // PUSH1 OP
public void testPUSH1(){
@ -817,6 +816,73 @@ public class VMTest {
}
@Test // SGT OP
public void testSGT_1(){
VM vm = new VM();
Program program = new Program(Hex.decode("600160020D"), new ProgramInvokeMockImpl());
String expected = "0000000000000000000000000000000000000000000000000000000000000001";
vm.step(program);
vm.step(program);
vm.step(program);
assertEquals(expected, Hex.toHexString(program.stack.peek().data).toUpperCase());
}
@Test // SGT OP
public void testSGT_2(){
VM vm = new VM();
Program program = new Program(Hex.decode("7F000000000000000000000000000000000000000000000000000000000000001E" + // 30
"7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF56" + // -170
"0D"), new ProgramInvokeMockImpl());
String expected = "0000000000000000000000000000000000000000000000000000000000000000";
vm.step(program);
vm.step(program);
vm.step(program);
assertEquals(expected, Hex.toHexString(program.stack.peek().data).toUpperCase());
}
@Test // SGT OP
public void testSGT_3(){
VM vm = new VM();
Program program = new Program(Hex.decode("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF56" + // -170
"7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF57" + // -169
"0D"), new ProgramInvokeMockImpl());
String expected = "0000000000000000000000000000000000000000000000000000000000000001";
vm.step(program);
vm.step(program);
vm.step(program);
assertEquals(expected, Hex.toHexString(program.stack.peek().data).toUpperCase());
}
@Test // SGT OP mal
public void testSGT_4(){
VM vm = new VM();
Program program = new Program(Hex.decode("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF56" + // -170
"0D"), new ProgramInvokeMockImpl());
try {
vm.step(program);
vm.step(program);
vm.step(program);
} catch (RuntimeException e) {
assertTrue(program.isStopped());
return;
}
fail();
}
@Test // LT OP
public void testLT_1(){
@ -876,6 +942,74 @@ public class VMTest {
}
@Test // SLT OP
public void testSLT_1(){
VM vm = new VM();
Program program = new Program(Hex.decode("600160020C"), new ProgramInvokeMockImpl());
String expected = "0000000000000000000000000000000000000000000000000000000000000000";
vm.step(program);
vm.step(program);
vm.step(program);
assertEquals(expected, Hex.toHexString(program.stack.peek().data).toUpperCase());
}
@Test // SLT OP
public void testSLT_2(){
VM vm = new VM();
Program program = new Program(Hex.decode("7F000000000000000000000000000000000000000000000000000000000000001E" + // 30
"7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF56" + // -170
"0C"), new ProgramInvokeMockImpl());
String expected = "0000000000000000000000000000000000000000000000000000000000000001";
vm.step(program);
vm.step(program);
vm.step(program);
assertEquals(expected, Hex.toHexString(program.stack.peek().data).toUpperCase());
}
@Test // SLT OP
public void testSLT_3(){
VM vm = new VM();
Program program = new Program(Hex.decode("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF56" + // -170
"7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF57" + // -169
"0C"), new ProgramInvokeMockImpl());
String expected = "0000000000000000000000000000000000000000000000000000000000000000";
vm.step(program);
vm.step(program);
vm.step(program);
assertEquals(expected, Hex.toHexString(program.stack.peek().data).toUpperCase());
}
@Test // SLT OP mal
public void testSLT_4(){
VM vm = new VM();
Program program = new Program(Hex.decode("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF56" + // -170
"0D"), new ProgramInvokeMockImpl());
try {
vm.step(program);
vm.step(program);
vm.step(program);
} catch (RuntimeException e) {
assertTrue(program.isStopped());
return;
}
fail();
}
@Test // NEG OP
public void testNEG_1(){
@ -1743,6 +1877,7 @@ public class VMTest {
assertEquals(s_expected_1, Hex.toHexString(item1.data).toUpperCase());
}
@Test // DIV OP
public void testDIV_5(){
@ -1774,6 +1909,70 @@ public class VMTest {
}
@Test // SDIV OP
public void testSDIV_1(){
VM vm = new VM();
Program program = new Program(Hex.decode("6103E87FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC1805"), new ProgramInvokeMockImpl());
String s_expected_1 = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
vm.step(program);
vm.step(program);
vm.step(program);
DataWord item1 = program.stack.pop();
assertEquals(s_expected_1, Hex.toHexString(item1.data).toUpperCase());
}
@Test // SDIV OP
public void testSDIV_2(){
VM vm = new VM();
Program program = new Program(Hex.decode("60FF60FF05"), new ProgramInvokeMockImpl());
String s_expected_1 = "0000000000000000000000000000000000000000000000000000000000000001";
vm.step(program);
vm.step(program);
vm.step(program);
DataWord item1 = program.stack.pop();
assertEquals(s_expected_1, Hex.toHexString(item1.data).toUpperCase());
}
@Test // SDIV OP
public void testSDIV_3(){
VM vm = new VM();
Program program = new Program(Hex.decode("600060FF05"), new ProgramInvokeMockImpl());
String s_expected_1 = "0000000000000000000000000000000000000000000000000000000000000000";
vm.step(program);
vm.step(program);
vm.step(program);
DataWord item1 = program.stack.pop();
assertEquals(s_expected_1, Hex.toHexString(item1.data).toUpperCase());
}
@Test // SDIV OP mal
public void testSDIV_4(){
VM vm = new VM();
Program program = new Program(Hex.decode("60FF05"), new ProgramInvokeMockImpl());
try {
vm.step(program);
vm.step(program);
} catch (RuntimeException e) {
assertTrue(program.isStopped());
return;
}
fail();
}
@Test // SUB OP
public void testSUB_1(){
@ -2480,6 +2679,128 @@ public class VMTest {
}
@Test // MOD OP
public void testMOD_1(){
VM vm = new VM();
Program program = new Program(Hex.decode("6003600406"), new ProgramInvokeMockImpl());
String s_expected_1 = "0000000000000000000000000000000000000000000000000000000000000001";
vm.step(program);
vm.step(program);
vm.step(program);
DataWord item1 = program.stack.pop();
assertEquals(s_expected_1, Hex.toHexString(item1.data).toUpperCase());
}
@Test // MOD OP
public void testMOD_2(){
VM vm = new VM();
Program program = new Program(Hex.decode("61012C6101F406"), new ProgramInvokeMockImpl());
String s_expected_1 = "00000000000000000000000000000000000000000000000000000000000000C8";
vm.step(program);
vm.step(program);
vm.step(program);
DataWord item1 = program.stack.pop();
assertEquals(s_expected_1, Hex.toHexString(item1.data).toUpperCase());
}
@Test // MOD OP
public void testMOD_3(){
VM vm = new VM();
Program program = new Program(Hex.decode("6004600206"), new ProgramInvokeMockImpl());
String s_expected_1 = "0000000000000000000000000000000000000000000000000000000000000002";
vm.step(program);
vm.step(program);
vm.step(program);
DataWord item1 = program.stack.pop();
assertEquals(s_expected_1, Hex.toHexString(item1.data).toUpperCase());
}
@Test // MOD OP mal
public void testMOD_4(){
VM vm = new VM();
Program program = new Program(Hex.decode("600406"), new ProgramInvokeMockImpl());
try {
vm.step(program);
vm.step(program);
vm.step(program);
} catch (RuntimeException e) {
assertTrue(program.isStopped());
return;
}
fail();
}
@Test // SMOD OP
public void testSMOD_1(){
VM vm = new VM();
Program program = new Program(Hex.decode("6003600407"), new ProgramInvokeMockImpl());
String s_expected_1 = "0000000000000000000000000000000000000000000000000000000000000001";
vm.step(program);
vm.step(program);
vm.step(program);
DataWord item1 = program.stack.pop();
assertEquals(s_expected_1, Hex.toHexString(item1.data).toUpperCase());
}
@Test // SMOD OP
public void testSMOD_2(){
VM vm = new VM();
Program program = new Program(Hex.decode("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE2" + // -30
"7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF56" + // -170
"07"), new ProgramInvokeMockImpl());
String s_expected_1 = "0000000000000000000000000000000000000000000000000000000000000000";
vm.step(program);
vm.step(program);
vm.step(program);
DataWord item1 = program.stack.pop();
assertEquals(s_expected_1, Hex.toHexString(item1.data).toUpperCase());
}
@Test // SMOD OP
public void testSMOD_3(){
VM vm = new VM();
Program program = new Program(Hex.decode("7F000000000000000000000000000000000000000000000000000000000000001E" + // 30
"7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF56" + // -170
"07"), new ProgramInvokeMockImpl());
String s_expected_1 = "000000000000000000000000000000000000000000000000000000000000000A";
vm.step(program);
vm.step(program);
vm.step(program);
DataWord item1 = program.stack.pop();
assertEquals(s_expected_1, Hex.toHexString(item1.data).toUpperCase());
}
@Test // SMOD OP mal
public void testSMOD_4(){
VM vm = new VM();
Program program = new Program(Hex.decode("7F000000000000000000000000000000000000000000000000000000000000001E" + // 30
"07"), new ProgramInvokeMockImpl());
try {
vm.step(program);
vm.step(program);
} catch (Exception e) {
assertTrue(program.isStopped());
return;
}
fail();
}
/* TEST CASE LIST END */
@ -2498,19 +2819,18 @@ public class VMTest {
}
// todo: add gas expeted and calculated to all test cases
// todo: considering: G_TXDATA + G_TRANSACTION
/**
* todo:
*
* 1) SDIV
* 2) MOD
* 3) SMOD
* 4) SLT
* 5) SGT
* 15) PREVHASH:
* 16) COINBASE:
* 17) TIMESTAMP:
@ -2524,7 +2844,7 @@ public class VMTest {
* 22) CREATE:
* 23) CALL:
*
* 24) SUICIDE:
*
**/