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:
parent
6ef4365a6d
commit
7fbd2964b5
|
@ -5,11 +5,9 @@ import org.ethereum.core.ContractDetails;
|
||||||
import org.ethereum.core.Transaction;
|
import org.ethereum.core.Transaction;
|
||||||
import org.ethereum.db.TrackDatabase;
|
import org.ethereum.db.TrackDatabase;
|
||||||
import org.ethereum.manager.WorldManager;
|
import org.ethereum.manager.WorldManager;
|
||||||
|
import org.ethereum.serpent.SerpentCompiler;
|
||||||
import org.ethereum.trie.TrackTrie;
|
import org.ethereum.trie.TrackTrie;
|
||||||
import org.ethereum.vm.Program;
|
import org.ethereum.vm.*;
|
||||||
import org.ethereum.vm.ProgramInvokeFactory;
|
|
||||||
import org.ethereum.vm.ProgramInvokeImpl;
|
|
||||||
import org.ethereum.vm.VM;
|
|
||||||
import org.spongycastle.util.encoders.Hex;
|
import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
@ -36,6 +34,24 @@ public class ProgramPlayDialog extends JPanel implements ActionListener,
|
||||||
|
|
||||||
private Transaction tx;
|
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) {
|
public ProgramPlayDialog(byte[] code, Transaction tx, Block lastBlock, ContractDetails contractDetails) {
|
||||||
|
|
||||||
this.tx = tx;
|
this.tx = tx;
|
||||||
|
@ -60,6 +76,10 @@ public class ProgramPlayDialog extends JPanel implements ActionListener,
|
||||||
trackStateDB.rollbackTrack();
|
trackStateDB.rollbackTrack();
|
||||||
|
|
||||||
|
|
||||||
|
doGUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doGUI(){
|
||||||
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
|
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
|
||||||
|
|
||||||
//Create the slider.
|
//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) {
|
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.
|
//Create and set up the window.
|
||||||
JFrame frame = new JFrame("Program Draft Play");
|
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:
|
//Schedule a job for the event-dispatching thread:
|
||||||
//creating and showing this application's GUI.
|
//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() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
createAndShowGUI();
|
createAndShowGUI(code, null, null, null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -204,18 +204,7 @@ public class SerpentToAssemblyCompiler extends SerpentBaseVisitor<String> {
|
||||||
// msg assigned has two arrays to calc
|
// msg assigned has two arrays to calc
|
||||||
if (ctx.msg_func() != null){
|
if (ctx.msg_func() != null){
|
||||||
|
|
||||||
String msgCode = visitMsg_func(ctx.msg_func());
|
String msgCode = visitMsg_func(ctx.msg_func(), varName);
|
||||||
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);
|
|
||||||
|
|
||||||
return msgCode;
|
return msgCode;
|
||||||
} else if (ctx.arr_def() != null){
|
} else if (ctx.arr_def() != null){
|
||||||
// if it's an array the all management is different
|
// 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);
|
return String.format("%s 32 MUL CALLDATALOAD ", operand0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public String visitMsg_func(@NotNull SerpentParser.Msg_funcContext ctx, String varName) {
|
||||||
public String visitMsg_func(@NotNull SerpentParser.Msg_funcContext ctx) {
|
|
||||||
|
|
||||||
// msg_func: 'msg' '(' int_val ',' int_val ',' int_val ',' arr_def ',' int_val ',' int_val')' ;
|
// 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]')' ;
|
// msg_func: 'msg' '(' [gas] ',' [to] ',' [value] ',' arr_def ',' [in_len] ',' [out_len]')' ;
|
||||||
|
|
||||||
String operand0 = visit(ctx.int_val(0));
|
String operand0 = visit(ctx.int_val(0));
|
||||||
String operand1 = visit(ctx.int_val(1));
|
String operand1 = visit(ctx.int_val(1));
|
||||||
String operand2 = visit(ctx.int_val(2));
|
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));
|
|
||||||
|
|
||||||
// OUTDATASIZE OUTDATASTART INDATASIZE INDATASTART VALUE TO GAS CALL
|
String loadInData = visit(ctx.arr_def());
|
||||||
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 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
|
@Override
|
||||||
|
@ -685,8 +698,6 @@ public class SerpentToAssemblyCompiler extends SerpentBaseVisitor<String> {
|
||||||
return (new BigInteger(1, numberBytes)).toString();
|
return (new BigInteger(1, numberBytes)).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static class UnknownOperandException extends RuntimeException {
|
public static class UnknownOperandException extends RuntimeException {
|
||||||
public UnknownOperandException(String name) {
|
public UnknownOperandException(String name) {
|
||||||
super("unknown operand: " + 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){
|
private Integer getArraySize(String code){
|
||||||
|
|
||||||
String result = "0";
|
String result = "0";
|
||||||
|
|
|
@ -244,7 +244,7 @@ public class Program {
|
||||||
|
|
||||||
AccountState receiverState;
|
AccountState receiverState;
|
||||||
byte[] accountData = result.getStateDb().get(toAddress);
|
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));
|
logger.info("no saved address in db to call: address={}" ,Hex.toHexString(toAddress));
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -26,6 +26,7 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
|
||||||
|
|
||||||
ContractDetails details = null;
|
ContractDetails details = null;
|
||||||
|
|
||||||
|
String ownerAddress;
|
||||||
|
|
||||||
|
|
||||||
public ProgramInvokeMockImpl(byte[] msgDataRaw){
|
public ProgramInvokeMockImpl(byte[] msgDataRaw){
|
||||||
|
@ -38,7 +39,7 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
|
||||||
/* ADDRESS op */
|
/* ADDRESS op */
|
||||||
public DataWord getOwnerAddress(){
|
public DataWord getOwnerAddress(){
|
||||||
|
|
||||||
byte[] addr = Hex.decode("77045e71a7a2c50903d88e564cd72fab11e82051");
|
byte[] addr = Hex.decode(ownerAddress);
|
||||||
return new DataWord(addr);
|
return new DataWord(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,6 +188,11 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
|
||||||
this.details = details;
|
this.details = details;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setOwnerAddress(String ownerAddress) {
|
||||||
|
this.ownerAddress = ownerAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<DataWord, DataWord> getStorage() {
|
public Map<DataWord, DataWord> getStorage() {
|
||||||
if (details == null) return null;
|
if (details == null) return null;
|
|
@ -1329,7 +1329,7 @@ public class SerpentCompileTest {
|
||||||
String code = "\n" +
|
String code = "\n" +
|
||||||
"a = msg(1, 2, 3, [11, 22, 33], 3, 6) \n" +
|
"a = msg(1, 2, 3, [11, 22, 33], 3, 6) \n" +
|
||||||
"b = a[0]\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);
|
String asmResult = SerpentCompiler.compile(code);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.ethereum.vm;
|
package org.ethereum.vm;
|
||||||
|
|
||||||
|
import org.abego.treelayout.internal.util.Contract;
|
||||||
import org.ethereum.core.AccountState;
|
import org.ethereum.core.AccountState;
|
||||||
import org.ethereum.core.ContractDetails;
|
import org.ethereum.core.ContractDetails;
|
||||||
import org.ethereum.crypto.HashUtil;
|
import org.ethereum.crypto.HashUtil;
|
||||||
|
@ -82,6 +83,8 @@ public class VMComplexTest {
|
||||||
pi.setChainDb(chainDb);
|
pi.setChainDb(chainDb);
|
||||||
pi.setStateDB(stateDB);
|
pi.setStateDB(stateDB);
|
||||||
pi.setDetails(contractDetails);
|
pi.setDetails(contractDetails);
|
||||||
|
pi.setOwnerAddress("77045e71a7a2c50903d88e564cd72fab11e82051");
|
||||||
|
|
||||||
|
|
||||||
// Play the program
|
// Play the program
|
||||||
VM vm = new VM();
|
VM vm = new VM();
|
||||||
|
@ -104,10 +107,119 @@ public class VMComplexTest {
|
||||||
System.out.println("*** Used gas: " + program.result.getGasUsed());
|
System.out.println("*** Used gas: " + program.result.getGasUsed());
|
||||||
System.out.println("*** Contract Balance: " + as.getBalance());
|
System.out.println("*** Contract Balance: " + as.getBalance());
|
||||||
|
|
||||||
|
// todo: assert caller balance after contract exec
|
||||||
|
|
||||||
assertEquals(expectedGas, program.result.getGasUsed());
|
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());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue