Remove POST / rename CALLSTATELESS to CALLCODE + add unit test and switch EXTCODESIZE and EXTCODECOPY

This commit is contained in:
nicksavers 2014-10-16 01:38:30 +02:00
parent ee442c4e34
commit e0e99baa29
8 changed files with 151 additions and 135 deletions

View File

@ -83,8 +83,10 @@ public class TestRunner {
long gaslimit = new BigInteger(env.getCurrentGasLimit()).longValue();
// Origin and caller need to exist in order to be able to execute
repository.createAccount(origin);
repository.createAccount(caller);
if(repository.getAccountState(origin) == null)
repository.createAccount(origin);
if(repository.getAccountState(caller) == null)
repository.createAccount(caller);
ProgramInvoke programInvoke = new ProgramInvokeImpl(address, origin, caller, balance,
gasPrice, gas, callValue, msgData, lastHash, coinbase,

View File

@ -101,12 +101,13 @@ public enum OpCode {
/** (0x3a) Get price of gas in current
* environment */
GASPRICE(0x3a),
/** (0x3b) Copy code running in current
* environment to memory with given offset */
EXTCODECOPY(0x3b),
/** (0x3c) Get size of code running in
/** (0x3b) Get size of code running in
* current environment with given offset */
EXTCODESIZE(0x3c),
EXTCODESIZE(0x3b),
/** (0x3c) Copy code running in current
* environment to memory with given offset */
EXTCODECOPY(0x3c),
/* Block Information */
@ -149,6 +150,8 @@ public enum OpCode {
MSIZE(0x5b),
/** (0x5c) Get the amount of available gas */
GAS(0x5c),
/** (0x5d) */
JUMPDEST(0x5d),
/* Push Operations */
@ -296,16 +299,9 @@ public enum OpCode {
CALL(0xf1), // [out_data_size] [out_data_start] [in_data_size] [in_data_start] [value] [to_addr] [gas] CALL
/** (0xf2) Halt execution returning output data */
RETURN(0xf2),
/** (0xf3) Same as call, except 5 arguments in
* and 0 arguments out, and instead of immediately
* calling it adds the call to a postqueue, to be
* executed after everything else
* (including prior-created posts) within the scope
* of that transaction execution is executed */
POST(0xf3),
/** (0xf4) Calls self, but grabbing the code from the
/** (0xf3) Calls self, but grabbing the code from the
* TO argument instead of from one's own address */
CALLSTATELESS(0xf4),
CALLCODE(0xf3),
/** (0xff) Halt execution and register account for
* later deletion */
SUICIDE(0xff);

View File

@ -14,9 +14,7 @@ import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Stack;
/**
@ -31,14 +29,6 @@ public class Program {
private int invokeHash;
private ProgramListener listener;
/**
* Instead of immediately executing a POST message call,
* it is added to a post-queue, to be executed after everything else
* (including prior-created posts) within the scope
* of that transaction execution is executed.
*/
private static Queue<MessageCall> messageQueue = new LinkedList<>();
Stack<DataWord> stack = new Stack<>();
ByteBuffer memory = null;
DataWord programAddress;
@ -344,10 +334,6 @@ public class Program {
}
}
}
public Queue<MessageCall> getMessageQueue() {
return messageQueue;
}
/**
* That method is for internal code invocations
@ -378,7 +364,7 @@ public class Program {
if (this.getGas().longValue() - msg.getGas().longValue() < 0 ) {
logger.info("No gas for the internal call, \n" +
"fromAddress={}, codeAddress={}",
Hex.toHexString(senderAddress), Hex.toHexString(contextAddress));
Hex.toHexString(senderAddress), Hex.toHexString(codeAddress));
this.stackPushZero();
return;
}
@ -477,6 +463,10 @@ public class Program {
throw new OutOfGasException();
result.spendGas(gasValue);
}
public void spendAllGas() {
spendGas(invokeData.getGas().longValue() - result.getGasUsed(), "Spending all remaining");
}
public void refundGas(long gasValue, String cause) {
gasLogger.info("[{}] Refund for cause: [{}], gas: [{}]", invokeHash, cause, gasValue);

View File

@ -17,9 +17,12 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
private Repository repository = null;
private String ownerAddress = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
private String contractAddress = "471fd3ad3e9eeadeec4608b92d16ce6b500704cc";
private byte[] ownerAddress = Hex.decode("cd2a3d9f938e13cd947ec05abc7fe734df8dd826");
private byte[] contractAddress = Hex.decode("471fd3ad3e9eeadeec4608b92d16ce6b500704cc");
// default for most tests. This can be overwritten by the test
private long gasLimit = 1000000;
public ProgramInvokeMockImpl(byte[] msgDataRaw) {
this();
this.msgData = msgDataRaw;
@ -27,10 +30,10 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
public ProgramInvokeMockImpl() {
this.repository = new RepositoryImpl("blockchainMoc", "detailsMoc", "stateMoc");
this.repository.createAccount(Hex.decode(ownerAddress));
this.repository.createAccount(ownerAddress);
this.repository.createAccount(Hex.decode(contractAddress));
this.repository.saveCode(Hex.decode(contractAddress),
this.repository.createAccount(contractAddress);
this.repository.saveCode(contractAddress,
Hex.decode("385E60076000396000605f556014600054601e60"
+ "205463abcddcba6040545b51602001600a525451"
+ "6040016014525451606001601e52545160800160"
@ -45,8 +48,7 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
/* ADDRESS op */
public DataWord getOwnerAddress() {
byte[] addr = Hex.decode(ownerAddress);
return new DataWord(addr);
return new DataWord(ownerAddress);
}
/* BALANCE op */
@ -82,8 +84,8 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
/* GAS op */
public DataWord getGas() {
byte[] minGasPrice = Hex.decode("0F4240");
return new DataWord(minGasPrice);
return new DataWord(gasLimit);
}
/* CALLVALUE op */
@ -170,11 +172,14 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
@Override
public DataWord getGaslimit() {
long gasLimit = 1000000;
return new DataWord(gasLimit);
}
public void setGasLimit(long gasLimit) {
this.gasLimit = gasLimit;
}
public void setOwnerAddress(String ownerAddress) {
public void setOwnerAddress(byte[] ownerAddress) {
this.ownerAddress = ownerAddress;
}

View File

@ -152,7 +152,7 @@ public class VM {
program.stackRequire(4);
newMemSize = memNeeded(stack.get(stack.size()-2), stack.get(stack.size()-4));
break;
case CALL: case CALLSTATELESS:
case CALL: case CALLCODE:
program.stackRequire(7);
gasCost = GasCost.CALL;
DataWord callGasWord = stack.get(stack.size()-1);
@ -164,10 +164,6 @@ public class VM {
BigInteger out = memNeeded(stack.get(stack.size()-6), stack.get(stack.size()-7)); // out offset+size
newMemSize = in.max(out);
break;
case POST:
program.stackRequire(5);
// TODO calculate POST gas cost
break;
case CREATE:
program.stackRequire(3);
gasCost = GasCost.CREATE;
@ -804,6 +800,8 @@ public class VM {
case JUMP:{
program.stackRequire(1);
DataWord pos = program.stackPop();
// if (!pos.equals(DataWord.ZERO) && OpCode.code(program.getCurrentOp()) != OpCode.JUMPDEST)
// throw new BadJumpDestinationException();
if (logger.isInfoEnabled())
hint = "~> " + pos.value();
@ -814,10 +812,12 @@ public class VM {
program.stackRequire(2);
DataWord pos = program.stackPop();
DataWord cond = program.stackPop();
// if (!pos.isZero() && OpCode.code(program.getCurrentOp()) != OpCode.JUMPDEST)
// throw new BadJumpDestinationException();
if (!cond.isZero()) {
program.setPC(pos);
} else{
} else {
program.step();
}
@ -867,6 +867,8 @@ public class VM {
program.stackPush(data);
} break;
case JUMPDEST:{
} break;
case CREATE:{
program.stackRequire(3);
DataWord value = program.stackPop();
@ -883,7 +885,7 @@ public class VM {
program.step();
} break;
case CALL: case CALLSTATELESS: {
case CALL: case CALLCODE: {
DataWord gas = program.stackPop();
DataWord codeAddress = program.stackPop();
DataWord value = program.stackPop();
@ -913,32 +915,6 @@ public class VM {
program.step();
} break;
case POST:{
program.stackRequire(5);
DataWord gas = program.stackPop();
DataWord codeAddress= program.stackPop();
DataWord value = program.stackPop();
DataWord inDataOffs = program.stackPop();
DataWord inDataSize = program.stackPop();
if (logger.isInfoEnabled()) {
hint = "addr: " + Hex.toHexString(codeAddress.getLast20Bytes())
+ " gas: " + gas.shortHex()
+ " inOff: " + inDataOffs.shortHex()
+ " inSize: " + inDataSize.shortHex();
logger.info(logString, program.getPC(),
String.format("%-12s", op.name()),
program.getGas().value(),
program.invokeData.getCallDeep(), hint);
}
MessageCall msgCall = new MessageCall(MsgType.POST, gas,
codeAddress, value, inDataOffs, inDataSize);
program.getMessageQueue().add(msgCall);
program.step();
} break;
case RETURN:{
program.stackRequire(2);
DataWord offset = program.stackPop();
@ -977,8 +953,10 @@ public class VM {
vmCounter++;
} catch (RuntimeException e) {
if(e instanceof OutOfGasException)
if(e instanceof OutOfGasException) {
logger.warn("OutOfGasException occurred", e);
program.spendAllGas();
}
else
logger.error("VM halted", e);
program.stop();
@ -1080,4 +1058,8 @@ public class VM {
gasBefore, gasCost, memWords);
}
}
@SuppressWarnings("serial")
public class BadJumpDestinationException extends RuntimeException {}
}

View File

@ -92,6 +92,4 @@ public class GitHubJSONTestSuiteTest {
Assert.assertTrue(result.isEmpty());
}
}
}

View File

@ -58,7 +58,7 @@ public class VMComplexTest {
accountState.setCodeHash(codeKey);
ProgramInvokeMockImpl pi = new ProgramInvokeMockImpl();
pi.setOwnerAddress("77045e71a7a2c50903d88e564cd72fab11e82051");
pi.setOwnerAddress(contractAddrB);
Repository repository = pi.getRepository();
repository.createAccount(callerAddrB);
@ -132,13 +132,13 @@ public class VMComplexTest {
byte[] contractA_addr_bytes = Hex.decode(contractA_addr);
byte[] codeA = Hex.decode(code_a);
ProgramInvokeMockImpl pi = new ProgramInvokeMockImpl();
pi.setOwnerAddress(contractB_addr);
Repository repository = pi.getRepository();
byte[] contractB_addr_bytes = Hex.decode(contractB_addr);
byte[] codeB = Hex.decode(code_b);
ProgramInvokeMockImpl pi = new ProgramInvokeMockImpl();
pi.setOwnerAddress(contractB_addr_bytes);
Repository repository = pi.getRepository();
repository.createAccount(contractA_addr_bytes);
repository.saveCode(contractA_addr_bytes, codeA);
@ -216,24 +216,16 @@ public class VMComplexTest {
long expectedVal_6 = 66;
// Set contract into Database
String callerAddr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
byte[] caller_addr_bytes = Hex.decode("cd2a3d9f938e13cd947ec05abc7fe734df8dd826");
String contractA_addr = "77045e71a7a2c50903d88e564cd72fab11e82051";
String contractB_addr = "83c5541a6c8d2dbad642f385d8d06ca9b6c731ee";
byte[] contractA_addr_bytes = Hex.decode("77045e71a7a2c50903d88e564cd72fab11e82051");
byte[] contractB_addr_bytes = Hex.decode("83c5541a6c8d2dbad642f385d8d06ca9b6c731ee");
String code_a = "600b60005460166020546021604054602c6060546037608054604260a05460c06000f2";
String code_b = "6000601f5560e05b60e05b54600060c05b015560605b6020015b80602001600b9054806040016016905480606001602190546080905460007377045e71a7a2c50903d88e564cd72fab11e820516103e8f1602060000260a00160200153600054";
byte[] caller_addr_bytes = Hex.decode(callerAddr);
byte[] contractA_addr_bytes = Hex.decode(contractA_addr);
byte[] codeA = Hex.decode(code_a);
byte[] contractB_addr_bytes = Hex.decode(contractB_addr);
byte[] codeB = Hex.decode(code_b);
byte[] codeA = Hex.decode("600b60005460166020546021604054602c6060546037608054604260a05460c06000f2");
byte[] codeB = Hex.decode("6000601f5560e05b60e05b54600060c05b015560605b6020015b80602001600b9054806040016016905480606001602190546080905460007377045e71a7a2c50903d88e564cd72fab11e820516103e8f1602060000260a00160200153600054");
ProgramInvokeMockImpl pi = new ProgramInvokeMockImpl();
pi.setOwnerAddress(contractB_addr);
pi.setOwnerAddress(contractB_addr_bytes);
Repository repository = pi.getRepository();
repository.createAccount(contractA_addr_bytes);
repository.saveCode(contractA_addr_bytes, codeA);
@ -303,25 +295,20 @@ public class VMComplexTest {
*/
// Set contract into Database
String callerAddr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
byte[] caller_addr_bytes = Hex.decode("cd2a3d9f938e13cd947ec05abc7fe734df8dd826");
String contractA_addr = "77045e71a7a2c50903d88e564cd72fab11e82051";
byte[] contractA_addr_bytes = Hex.decode("77045e71a7a2c50903d88e564cd72fab11e82051");
String code_a = "7f7f60c860005461012c602054000000000000" +
byte[] codeA = Hex.decode("7f7f60c860005461012c602054000000000000" +
"00000000000000000000000000006000547e60" +
"005460206000f2000000000000000000000000" +
"0000000000000000000000602054602960006064f0";
"0000000000000000000000602054602960006064f0");
ProgramInvokeMockImpl pi = new ProgramInvokeMockImpl();
pi.setOwnerAddress(contractA_addr);
pi.setOwnerAddress(contractA_addr_bytes);
Repository repository = pi.getRepository();
byte[] caller_addr_bytes = Hex.decode(callerAddr);
byte[] contractA_addr_bytes = Hex.decode(contractA_addr);
byte[] codeA = Hex.decode(code_a);
repository.createAccount(contractA_addr_bytes);
repository.saveCode(contractA_addr_bytes, codeA);
@ -354,22 +341,80 @@ public class VMComplexTest {
// TODO CALL contract with gas > gasRemaining && gas > Long.MAX_VALUE
}
@Test // CALLSTATELESS contract
@Ignore
@Test // contractB call itself with code from contractA
public void test6() {
// TODO
}
@Test // POST contract
@Ignore
public void test7() {
// TODO
}
@Test // POST after tx runs out of gas
@Ignore
public void test8() {
// TODO
}
/**
* #The code will run
* ------------------
contract A: 945304eb96065b2a98b57a48a06ae28d285a71b5
---------------
PUSH1 0 CALLDATALOAD SLOAD NOT PUSH1 9 JUMPI STOP
PUSH1 32 CALLDATALOAD PUSH1 0 CALLDATALOAD SSTORE
contract B: 0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6
-----------
{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
(MSTORE 32 0xaaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa)
[[ 0 ]] (CALLSTATELESS 1000000 0x945304eb96065b2a98b57a48a06ae28d285a71b5 23 0 64 64 0)
}
*/
// Set contract into Database
byte[] caller_addr_bytes = Hex.decode("cd1722f3947def4cf144679da39c4c32bdc35681");
byte[] contractA_addr_bytes = Hex.decode("945304eb96065b2a98b57a48a06ae28d285a71b5");
byte[] contractB_addr_bytes = Hex.decode("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6");
byte[] codeA = Hex.decode("600035560f6009590060203560003557");
byte[] codeB = Hex.decode("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6000547faaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa6020546000604060406000601773945304eb96065b2a98b57a48a06ae28d285a71b5620f4240f3600057");
ProgramInvokeMockImpl pi = new ProgramInvokeMockImpl();
pi.setOwnerAddress(contractB_addr_bytes);
pi.setGasLimit(10000000000000l);
Repository repository = pi.getRepository();
repository.createAccount(contractA_addr_bytes);
repository.saveCode(contractA_addr_bytes, codeA);
repository.addBalance(contractA_addr_bytes, BigInteger.valueOf(23));
repository.createAccount(contractB_addr_bytes);
repository.saveCode(contractB_addr_bytes, codeB);
repository.addBalance(contractB_addr_bytes, new BigInteger("1000000000000000000"));
repository.createAccount(caller_addr_bytes);
repository.addBalance(caller_addr_bytes, new BigInteger("100000000000000000000"));
// ****************** //
// Play the program //
// ****************** //
VM vm = new VM();
Program program = new Program(codeB, pi);
try {
while(!program.isStopped())
vm.step(program);
} catch (RuntimeException e) {
program.setRuntimeFailure(e);
}
System.out.println();
System.out.println("============ Results ============");
System.out.println("*** Used gas: " + program.result.getGasUsed());
DataWord memValue1 = program.memoryLoad(new DataWord(0));
DataWord memValue2 = program.memoryLoad(new DataWord(32));
DataWord storeValue1 = repository.getStorageValue(contractB_addr_bytes, new DataWord(00));
repository.close();
assertEquals("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", memValue1.toString());
assertEquals("aaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa", memValue2.toString());
assertEquals("0x1", storeValue1.shortHex());
// TODO: check that the value pushed after exec is 1
}
}

View File

@ -2546,7 +2546,7 @@ public class VMTest {
VM vm = new VM();
Program program =
new Program(Hex.decode("60036007600073471FD3AD3E9EEADEEC4608B92D16CE6B500704CC3B123456"), new ProgramInvokeMockImpl());
new Program(Hex.decode("60036007600073471FD3AD3E9EEADEEC4608B92D16CE6B500704CC3C123456"), new ProgramInvokeMockImpl());
String m_expected_1 = "6000600000000000000000000000000000000000000000000000000000000000";
vm.step(program);
@ -2564,7 +2564,7 @@ public class VMTest {
VM vm = new VM();
Program program =
new Program(Hex.decode("603E6007600073471FD3AD3E9EEADEEC4608B92D16CE6B500704CC3B6000605f556014600054601e60205463abcddcba6040545b51602001600a5254516040016014525451606001601e5254516080016028525460a052546016604860003960166000f26000603f556103e75660005460005360200235602054"),
new Program(Hex.decode("603E6007600073471FD3AD3E9EEADEEC4608B92D16CE6B500704CC3C6000605f556014600054601e60205463abcddcba6040545b51602001600a5254516040016014525451606001601e5254516080016028525460a052546016604860003960166000f26000603f556103e75660005460005360200235602054"),
new ProgramInvokeMockImpl());
String m_expected_1 = "6000605F556014600054601E60205463ABCDDCBA6040545B51602001600A5254516040016014525451606001601E5254516080016028525460A0525460160000";
@ -2582,7 +2582,7 @@ public class VMTest {
public void testEXTCODECOPY_3() {
VM vm = new VM();
Program program =
new Program(Hex.decode("605E6007600073471FD3AD3E9EEADEEC4608B92D16CE6B500704CC3B6000605f556014600054601e60205463abcddcba6040545b51602001600a5254516040016014525451606001601e5254516080016028525460a052546016604860003960166000f26000603f556103e75660005460005360200235"),
new Program(Hex.decode("605E6007600073471FD3AD3E9EEADEEC4608B92D16CE6B500704CC3C6000605f556014600054601e60205463abcddcba6040545b51602001600a5254516040016014525451606001601e5254516080016028525460a052546016604860003960166000f26000603f556103e75660005460005360200235"),
new ProgramInvokeMockImpl());
vm.step(program);
@ -2600,7 +2600,7 @@ public class VMTest {
VM vm = new VM();
Program program =
new Program(Hex.decode("605E6007600073471FD3AD3E9EEADEEC4608B92D16CE6B500704CC3B6000605f556014600054601e60205463abcddcba6040545b51602001600a5254516040016014525451606001601e5254516080016028525460a052546016604860003960166000f26000603f556103e756600054600053602002351234"),
new Program(Hex.decode("605E6007600073471FD3AD3E9EEADEEC4608B92D16CE6B500704CC3C6000605f556014600054601e60205463abcddcba6040545b51602001600a5254516040016014525451606001601e5254516080016028525460a052546016604860003960166000f26000603f556103e756600054600053602002351234"),
new ProgramInvokeMockImpl());
vm.step(program);
@ -2617,7 +2617,7 @@ public class VMTest {
public void testEXTCODECOPY_5() {
VM vm = new VM();
Program program =
new Program(Hex.decode("611234600054615566602054603E6000602073471FD3AD3E9EEADEEC4608B92D16CE6B500704CC3B6000605f556014600054601e60205463abcddcba6040545b51602001600a5254516040016014525451606001601e5254516080016028525460a052546016604860003960166000f26000603f556103e756600054600053602002351234"),
new Program(Hex.decode("611234600054615566602054603E6000602073471FD3AD3E9EEADEEC4608B92D16CE6B500704CC3C6000605f556014600054601e60205463abcddcba6040545b51602001600a5254516040016014525451606001601e5254516080016028525460a052546016604860003960166000f26000603f556103e756600054600053602002351234"),
new ProgramInvokeMockImpl());
vm.step(program);
@ -2641,7 +2641,7 @@ public class VMTest {
public void testEXTCODECOPY_6() {
VM vm = new VM();
Program program =
new Program(Hex.decode("605E600773471FD3AD3E9EEADEEC4608B92D16CE6B500704CC3B"),
new Program(Hex.decode("605E600773471FD3AD3E9EEADEEC4608B92D16CE6B500704CC3C"),
new ProgramInvokeMockImpl());
try {
vm.step(program);
@ -3359,17 +3359,15 @@ public class VMTest {
public ProgramInvoke createProgramInvoke_1() {
String ownerAddress = "77045E71A7A2C50903D88E564CD72FAB11E82051";
byte[] address = Hex.decode(ownerAddress);
byte[] ownerAddress = Hex.decode("77045E71A7A2C50903D88E564CD72FAB11E82051");
byte[] msgData = Hex.decode("00000000000000000000000000000000000000000000000000000000000000A1" +
"00000000000000000000000000000000000000000000000000000000000000B1");
ProgramInvokeMockImpl pi = new ProgramInvokeMockImpl(msgData);
pi.setOwnerAddress(ownerAddress);
pi.getRepository().createAccount(address);
pi.getRepository().addBalance(address, BigInteger.valueOf(1000L));
pi.getRepository().createAccount(ownerAddress);
pi.getRepository().addBalance(ownerAddress, BigInteger.valueOf(1000L));
return pi;
}