CALL with data in:

+ ProgramPlayDialog adjust for simple code play (stand alone run)
+ msg - compile fixed to generated the right code
+ VMComplexTest to test contractA --> ContractB with data [11, 22]
This commit is contained in:
romanman 2014-06-22 18:22:18 +01:00
parent 6ef4365a6d
commit 7fbd2964b5
6 changed files with 196 additions and 37 deletions

View File

@ -5,11 +5,9 @@ import org.ethereum.core.ContractDetails;
import org.ethereum.core.Transaction;
import org.ethereum.db.TrackDatabase;
import org.ethereum.manager.WorldManager;
import org.ethereum.serpent.SerpentCompiler;
import org.ethereum.trie.TrackTrie;
import org.ethereum.vm.Program;
import org.ethereum.vm.ProgramInvokeFactory;
import org.ethereum.vm.ProgramInvokeImpl;
import org.ethereum.vm.VM;
import org.ethereum.vm.*;
import org.spongycastle.util.encoders.Hex;
import javax.swing.*;
@ -36,6 +34,24 @@ public class ProgramPlayDialog extends JPanel implements ActionListener,
private Transaction tx;
public ProgramPlayDialog(byte[] code){
outputList = new ArrayList<String>();
VM vm = new VM();
ProgramInvoke pi = new ProgramInvokeMockImpl();
Program program = new Program(code ,
pi);
program.addListener(this);
program.fullTrace();
vm.play(program);
doGUI();
}
public ProgramPlayDialog(byte[] code, Transaction tx, Block lastBlock, ContractDetails contractDetails) {
this.tx = tx;
@ -60,6 +76,10 @@ public class ProgramPlayDialog extends JPanel implements ActionListener,
trackStateDB.rollbackTrack();
doGUI();
}
public void doGUI(){
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
//Create the slider.
@ -137,7 +157,12 @@ public class ProgramPlayDialog extends JPanel implements ActionListener,
*/
public static void createAndShowGUI(byte[] runCode, Transaction tx, Block lastBlock, ContractDetails details) {
ProgramPlayDialog ppd = new ProgramPlayDialog(runCode, tx, lastBlock, details);
ProgramPlayDialog ppd;
if (tx != null)
ppd = new ProgramPlayDialog(runCode, tx, lastBlock, details);
else{
ppd = new ProgramPlayDialog(runCode);
}
//Create and set up the window.
JFrame frame = new JFrame("Program Draft Play");
@ -173,13 +198,15 @@ public class ProgramPlayDialog extends JPanel implements ActionListener,
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
/* todo: make dummy tx for dialog single invokation
String asmCode ="0 31 MSTORE8 224 MSIZE 224 MSIZE MSTORE 0 192 MSIZE ADD MSTORE8 96 MSIZE 32 ADD MSIZE DUP 32 ADD 11 SWAP MSTORE DUP 64 ADD 22 SWAP MSTORE DUP 96 ADD 33 SWAP MSTORE 128 SWAP MSTORE 0 752278364205682983151548199104072833112320979438 1000 CALL 32 0 MUL 160 ADD 32 ADD MLOAD 0 MSTORE";
final byte[] code = SerpentCompiler.compileAssemblyToMachine(asmCode);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
createAndShowGUI(code, null, null, null);
}
});
*/
}
}

View File

@ -204,18 +204,7 @@ public class SerpentToAssemblyCompiler extends SerpentBaseVisitor<String> {
// msg assigned has two arrays to calc
if (ctx.msg_func() != null){
String msgCode = visitMsg_func(ctx.msg_func());
int outSize = getMsgOutputArraySize(msgCode);
int inSize = getMsgInputArraySize(msgCode);
msgCode = cleanMsgString(msgCode);
String randomArrayName = new String(HashUtil.randomPeerId());
arraysSize.put(randomArrayName, inSize * 32 + 32);
arraysIndex.add(randomArrayName);
arraysSize.put(varName, outSize * 32 + 32);
arraysIndex.add(varName);
String msgCode = visitMsg_func(ctx.msg_func(), varName);
return msgCode;
} else if (ctx.arr_def() != null){
// if it's an array the all management is different
@ -599,22 +588,46 @@ public class SerpentToAssemblyCompiler extends SerpentBaseVisitor<String> {
return String.format("%s 32 MUL CALLDATALOAD ", operand0);
}
@Override
public String visitMsg_func(@NotNull SerpentParser.Msg_funcContext ctx) {
public String visitMsg_func(@NotNull SerpentParser.Msg_funcContext ctx, String varName) {
// msg_func: 'msg' '(' int_val ',' int_val ',' int_val ',' arr_def ',' int_val ',' int_val')' ;
// msg_func: 'msg' '(' [gas] ',' [to] ',' [value] ',' arr_def ',' [in_len] ',' [out_len]')' ;
String operand0 = visit(ctx.int_val(0));
String operand1 = visit(ctx.int_val(1));
String operand2 = visit(ctx.int_val(2));
String operand3 = visit(ctx.arr_def());
String operand4 = visit(ctx.int_val(3));
String operand5 = visit(ctx.int_val(4));
String operand0 = visit(ctx.int_val(0));
String operand1 = visit(ctx.int_val(1));
String operand2 = visit(ctx.int_val(2));
// OUTDATASIZE OUTDATASTART INDATASIZE INDATASTART VALUE TO GAS CALL
return String.format("<out_size %s out_size> <in_size %s in_size> %s %s %s %s %s %s CALL ",
operand5, operand4, operand5, operand4, operand3, operand2, operand1, operand0);
String loadInData = visit(ctx.arr_def());
String inSizeCallParam = visit(ctx.int_val(3));
String outSizeCallParam = visit(ctx.int_val(4));
// todo: 1. allocate out_memory and push ptr
// todo: 2. push ptr for in_memory allocated
String randomArrayName = new String(HashUtil.randomPeerId());
int inSize = Integer.parseInt( inSizeCallParam );
int outSize = Integer.parseInt( outSizeCallParam );
arraysSize.put(randomArrayName, inSize * 32 + 32);
arraysIndex.add(randomArrayName);
int outSizeVal = outSize * 32 + 32;
arraysSize.put(varName, outSize * 32 + 32);
arraysIndex.add(varName);
// [OUTDATASIZE] [OUTDATASTART] [INDATASIZE] [INDATASTART] [VALUE] [TO] [GAS] CALL
// [OUTDATASIZE] [OUTDATASTART] [INDATASIZE] [INDATASTART] ***ARR_IN_SET*** [VALUE] [TO] [GAS] CALL
//X_X = [ 32 + 128 + 6 * 32 ] = [ var_table_size + in_arr_size + out_arr_size ]
// this code allocates the memory block for the out data,
// and saves the size in typical array format [size, element_1, element_2, ...]
String outArrSet = String.format( " %d MSIZE MSTORE 0 %d MSIZE ADD MSTORE8 ", outSizeVal, outSizeVal - 32 );
return String.format("%d MSIZE %s %d %s %s %s %s CALL ",
outSizeVal, outArrSet ,inSize * 32, loadInData, operand2, operand1, operand0);
}
@Override
@ -685,8 +698,6 @@ public class SerpentToAssemblyCompiler extends SerpentBaseVisitor<String> {
return (new BigInteger(1, numberBytes)).toString();
}
public static class UnknownOperandException extends RuntimeException {
public UnknownOperandException(String name) {
super("unknown operand: " + name);
@ -739,7 +750,10 @@ public class SerpentToAssemblyCompiler extends SerpentBaseVisitor<String> {
}
/**
* After the array deff code is set
* extract the size out of code string
*/
private Integer getArraySize(String code){
String result = "0";

View File

@ -244,7 +244,7 @@ public class Program {
AccountState receiverState;
byte[] accountData = result.getStateDb().get(toAddress);
if (accountData == null){
if (accountData == null || accountData.length == 0){
logger.info("no saved address in db to call: address={}" ,Hex.toHexString(toAddress));
return;

View File

@ -26,6 +26,7 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
ContractDetails details = null;
String ownerAddress;
public ProgramInvokeMockImpl(byte[] msgDataRaw){
@ -38,7 +39,7 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
/* ADDRESS op */
public DataWord getOwnerAddress(){
byte[] addr = Hex.decode("77045e71a7a2c50903d88e564cd72fab11e82051");
byte[] addr = Hex.decode(ownerAddress);
return new DataWord(addr);
}
@ -187,6 +188,11 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
this.details = details;
}
public void setOwnerAddress(String ownerAddress) {
this.ownerAddress = ownerAddress;
}
@Override
public Map<DataWord, DataWord> getStorage() {
if (details == null) return null;

View File

@ -1329,7 +1329,7 @@ public class SerpentCompileTest {
String code = "\n" +
"a = msg(1, 2, 3, [11, 22, 33], 3, 6) \n" +
"b = a[0]\n" ;
String expected = "0 31 MSTORE8 6 3 MSIZE 32 ADD MSIZE DUP 32 ADD 11 SWAP MSTORE DUP 64 ADD 22 SWAP MSTORE DUP 96 ADD 33 SWAP MSTORE 128 SWAP MSTORE 3 2 1 CALL 32 0 MUL 160 ADD 32 ADD MLOAD 0 MSTORE";
String expected = "0 31 MSTORE8 MSIZE 32 ADD MSIZE DUP 32 ADD 11 SWAP MSTORE DUP 64 ADD 22 SWAP MSTORE DUP 96 ADD 33 SWAP MSTORE 128 SWAP MSTORE 6 3 3 2 1 CALL 32 0 MUL 160 ADD 32 ADD MLOAD 0 MSTORE";
String asmResult = SerpentCompiler.compile(code);

View File

@ -1,5 +1,6 @@
package org.ethereum.vm;
import org.abego.treelayout.internal.util.Contract;
import org.ethereum.core.AccountState;
import org.ethereum.core.ContractDetails;
import org.ethereum.crypto.HashUtil;
@ -82,6 +83,8 @@ public class VMComplexTest {
pi.setChainDb(chainDb);
pi.setStateDB(stateDB);
pi.setDetails(contractDetails);
pi.setOwnerAddress("77045e71a7a2c50903d88e564cd72fab11e82051");
// Play the program
VM vm = new VM();
@ -104,10 +107,119 @@ public class VMComplexTest {
System.out.println("*** Used gas: " + program.result.getGasUsed());
System.out.println("*** Contract Balance: " + as.getBalance());
// todo: assert caller balance after contract exec
assertEquals(expectedGas, program.result.getGasUsed());
}
@Test // contract call recursive with data
public void test2(){
/**
* #The code will run
* ------------------
contract A: 77045e71a7a2c50903d88e564cd72fab11e82051
---------------
a = msg.data[0]
b = msg.data[1]
contract.storage[a]
contract.storage[b]
contract B: 83c5541a6c8d2dbad642f385d8d06ca9b6c731ee
-----------
a = msg((tx.gas / 10 * 8), 0x77045e71a7a2c50903d88e564cd72fab11e82051, 0, [11, 22, 33], 3, 6)
*/
long expectedVal_1 = 11;
long expectedVal_2 = 22;
// Set contract into Database
String callerAddr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
String contractA_addr = "77045e71a7a2c50903d88e564cd72fab11e82051";
String contractB_addr = "83c5541a6c8d2dbad642f385d8d06ca9b6c731ee";
String code_a = "60006020023560005460016020023560205460005360005760205360015700";
String code_b = "6000601f5560e05b60e05b54600060c05b015560605b6020015b51602001600b5254516040016016525451606001602152546080525460007377045e71a7a2c50903d88e564cd72fab11e820516103e8f1602060000260a00160200153600054";
byte[] caller_addr_bytes = Hex.decode(callerAddr);
byte[] contractA_addr_bytes = Hex.decode(contractA_addr);
byte[] codeA = Hex.decode(code_a);
byte[] codeA_Key = HashUtil.sha3(codeA);
AccountState accountState_a = new AccountState();
accountState_a.setCodeHash(codeA_Key);
WorldManager.instance.worldState.update(contractA_addr_bytes, accountState_a.getEncoded());
byte[] contractB_addr_bytes = Hex.decode(contractB_addr);
byte[] codeB = Hex.decode(code_b);
byte[] codeB_Key = HashUtil.sha3(codeB);
AccountState accountState_b = new AccountState();
accountState_b.setCodeHash(codeB_Key);
WorldManager.instance.worldState.update(contractB_addr_bytes, accountState_a.getEncoded());
AccountState callerAcountState = new AccountState();
callerAcountState.addToBalance(new BigInteger("100000000000000000000"));
WorldManager.instance.worldState.update(caller_addr_bytes, callerAcountState.getEncoded());
WorldManager.instance.chainDB.put(codeA_Key, codeA);
TrackTrie stateDB = new TrackTrie(WorldManager.instance.worldState);
TrackDatabase chainDb = new TrackDatabase(WorldManager.instance.chainDB);
TrackDatabase detaildDB = new TrackDatabase(WorldManager.instance.detaildDB);
ProgramInvokeMockImpl pi = new ProgramInvokeMockImpl();
pi.setDetaildDB(detaildDB);
pi.setChainDb(chainDb);
pi.setStateDB(stateDB);
pi.setDetails(null);
pi.setOwnerAddress(contractB_addr);
// ****************** //
// 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 ============");
AccountState as =
new AccountState(WorldManager.instance.worldState.get(
Hex.decode( contractA_addr) ));
System.out.println("*** Used gas: " + program.result.getGasUsed());
byte[] rlpBytes = WorldManager.instance.detaildDB.get(contractA_addr_bytes);
ContractDetails details = new ContractDetails(rlpBytes);
DataWord value_1 = details.getStorage().get(new DataWord(00));
DataWord value_2 = details.getStorage().get(new DataWord(01));
assertEquals(expectedVal_1, value_1.longValue());
assertEquals(expectedVal_2, value_2.longValue());
}
}