VM impl, go on:

ADDRESS, BALANCE, ORIGIN, CALLER, CALLVALUE, CALLDATALOAD, CALLDATASIZE,
CALLDATACOPY, CODESIZE, CODECOPY implementation and unittest
This commit is contained in:
romanman 2014-06-03 19:30:34 +03:00
parent d348b552b3
commit 9bec5d6885
8 changed files with 1063 additions and 208 deletions

View File

@ -109,7 +109,7 @@ class ContractCallDialog extends JDialog implements MessageAwareDialog{
new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
submitContract();
submitContractCall();
}
}
);
@ -215,7 +215,7 @@ class ContractCallDialog extends JDialog implements MessageAwareDialog{
this.statusMsg.setText(text);
}
public void submitContract(){
public void submitContractCall(){
ClientPeer peer = MainData.instance.getActivePeer();
if (peer == null) {

View File

@ -1,6 +1,8 @@
package org.ethereum.gui;
import org.ethereum.vm.DataWord;
import org.ethereum.vm.Program;
import org.ethereum.vm.ProgramInvoke;
import org.ethereum.vm.VM;
import org.spongycastle.util.encoders.Hex;
@ -24,13 +26,15 @@ public class ProgramPlayDialog extends JPanel implements ActionListener, ChangeL
public List<String> outputList;
public JTextArea console;
public JSlider stepSlider;
public ProgramPlayDialog() {
outputList = new ArrayList<String>();
VM vm = new VM();
Program program = new Program(Hex.decode("630000000060445960CC60DD611234600054615566602054630000000060445960CC60DD611234600054615566602054630000000060445960CC60DD611234600054615566602054"));
// Program program = new Program(Hex.decode("630000000060445960CC60DD611234600054615566602054630000000060445960CC60DD611234600054615566602054630000000060445960CC60DD611234600054615566602054"));
Program program = new Program(Hex.decode("6000605f556014600054601e60205463abcddcba6040545b51602001600a5254516040016014525451606001601e5254516080016028525460a052546016604860003960166000f26000603f556103e75660005460005360200235602054"), null);
program.addListener(this);
program.fullTrace();
@ -40,7 +44,7 @@ public class ProgramPlayDialog extends JPanel implements ActionListener, ChangeL
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
//Create the slider.
JSlider stepSlider = new JSlider(JSlider.HORIZONTAL,
stepSlider = new JSlider(JSlider.HORIZONTAL,
0, outputList.size() - 1, 0);
@ -64,16 +68,22 @@ public class ProgramPlayDialog extends JPanel implements ActionListener, ChangeL
int i = stepSlider.getValue();
console = new JTextArea(outputList.get(i));
console.setFont(new Font("Courier New", Font.PLAIN, 13));
console.setForeground(new Color(143, 170, 220));
console.setForeground(new Color(183, 209, 253));
console.setBackground(Color.BLACK);
console.setLineWrap(true);
stepSlider.setFocusable(true);
add(console);
JScrollPane scrollPane = new JScrollPane(console);
add(scrollPane);
add(stepSlider);
this.setPreferredSize(new Dimension(600, 300));
}
public void setFocus(){
stepSlider.requestFocus();
}
@Override
@ -92,6 +102,7 @@ public class ProgramPlayDialog extends JPanel implements ActionListener, ChangeL
String out = outputList.get(i);
console.setText(out);
console.setCaretPosition(0);
}
@ -110,12 +121,18 @@ public class ProgramPlayDialog extends JPanel implements ActionListener, ChangeL
JFrame frame = new JFrame("SliderDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(600, 500));
frame.setLocation(400, 200);
//Add content to the window.
frame.add(ppd, BorderLayout.CENTER);
//Display the window.
frame.pack();
frame.setVisible(true);
ppd.setFocus();
}
@Override

View File

@ -58,7 +58,7 @@ public enum OpCode {
CALLDATASIZE(0x36),
CALLDATACOPY(0x37),
CODESIZE(0x38),
CODECOPY(0x39), // [len src_start target_start CODECOPY]
CODECOPY(0x39), // [len code_start mem_start CODECOPY]
GASPRICE(0x3a),
/**

View File

@ -26,14 +26,19 @@ public class Program {
Map<DataWord, DataWord> storage = new HashMap<DataWord, DataWord>();
ByteBuffer memory = null;
ByteBuffer hReturn = null;
byte[] ops;
int pc = 0;
boolean stopped = false;
public Program(byte[] ops) {
ProgramInvoke invokeData;
if (ops == null) throw new RuntimeException("program can not run with ops: null");
public Program(byte[] ops, ProgramInvoke invokeData) {
if (ops == null) throw new RuntimeException("program can not run with ops: null");
this.invokeData = invokeData;
this.ops = ops;
}
@ -58,7 +63,10 @@ public class Program {
public void setPC(DataWord pc) {
this.pc = pc.value().intValue();
if (this.pc > ops.length) throw new RuntimeException("pc overflow pc: " + pc);
if (this.pc > ops.length) {
stop();
throw new RuntimeException("pc overflow pc: " + pc);
}
}
public void setPC(int pc) {
@ -73,6 +81,10 @@ public class Program {
stopped = true;
}
public void setHReturn(ByteBuffer buff){
hReturn = buff;
}
public void step(){
++pc;
if (pc >= ops.length) stop();
@ -80,7 +92,10 @@ public class Program {
public byte[] sweep(int n){
if (pc + n > ops.length) throw new RuntimeException("pc overflow sweep n: " + n + " pc: " + pc);
if (pc + n > ops.length) {
stop();
throw new RuntimeException("pc overflow sweep n: " + n + " pc: " + pc);
}
byte[] data = Arrays.copyOfRange(ops, pc, pc + n);
pc += n;
@ -89,9 +104,12 @@ public class Program {
return data;
}
public DataWord stackPull(){
public DataWord stackPop(){
if (stack.size() == 0) throw new RuntimeException("attempted pull action for empty stack");
if (stack.size() == 0){
stop();
throw new RuntimeException("attempted pull action for empty stack");
}
return stack.pop();
};
@ -125,6 +143,21 @@ public class Program {
return new DataWord(data);
}
public ByteBuffer memoryChunk(DataWord offsetData, DataWord sizeData){
int offset = offsetData.value().intValue();
int size = sizeData.value().intValue();
byte[] chunk = new byte[size];
if (memory.limit() < offset + size) size = memory.limit() - offset;
System.arraycopy(memory.array(), offset, chunk, 0, size);
return ByteBuffer.wrap(chunk);
}
private void allocateMemory(int address, byte[] value){
int memSize = 0;
@ -166,6 +199,52 @@ public class Program {
}
public DataWord getOwnerAddress(){
if (invokeData == null) return new DataWord( new byte[0]);
return invokeData.getOwnerAddress();
}
public DataWord getBalance(){
if (invokeData == null) return new DataWord( new byte[0]);
return invokeData.getBalance();
}
public DataWord getOriginAddress(){
if (invokeData == null) return new DataWord( new byte[0]);
return invokeData.getOriginAddress();
}
public DataWord getCallerAddress(){
if (invokeData == null) return new DataWord( new byte[0]);
return invokeData.getCallerAddress();
}
public DataWord getMinGasPrice(){
if (invokeData == null) return new DataWord( new byte[0]);
return invokeData.getMinGasPrice();
}
public DataWord getCallValue(){
if (invokeData == null) return new DataWord( new byte[0]);
return invokeData.getCallValue();
}
public DataWord getDataSize(){
if (invokeData == null) return new DataWord( new byte[0]);
return invokeData.getDataSize();
}
public DataWord getDataValue(DataWord index){
if (invokeData == null) return new DataWord( new byte[0]);
return invokeData.getDataValue(index);
}
public byte[] getDataCopy(DataWord offset, DataWord length){
if (invokeData == null) return new byte[0];
return invokeData.getDataCopy(offset, length);
}
public DataWord storageLoad(DataWord key){
return storage.get(key);
}
@ -237,6 +316,10 @@ public class Program {
global.append(" -- MEMORY -- ").append(memoryData).append("\n");
global.append(" -- STORAGE -- ").append(storageData).append("\n");
if (hReturn != null){
global.append("\n HReturn: ").append(Hex.toHexString( hReturn.array()));
}
if (listener != null){
listener.output(global.toString());
}

View File

@ -0,0 +1,24 @@
package org.ethereum.vm;
/**
* www.ethereumJ.com
* User: Roman Mandeleil
* Created on: 03/06/2014 14:59
*/
public interface ProgramInvoke {
public DataWord getOwnerAddress();
public DataWord getBalance();
public DataWord getOriginAddress();
public DataWord getCallerAddress();
public DataWord getMinGasPrice();
public DataWord getCallValue();
public DataWord getDataSize();
public DataWord getDataValue(DataWord indexData);
public byte[] getDataCopy(DataWord offsetData, DataWord lengthData);
}

View File

@ -4,6 +4,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import static org.ethereum.vm.OpCode.PUSH1;
@ -37,13 +38,14 @@ public class VM {
case STOP:{
program.setHReturn(ByteBuffer.allocate(0));
program.stop();
}
break;
case ADD:{
DataWord word1 = program.stackPull();
DataWord word2 = program.stackPull();
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
word1.add(word2);
program.stackPush(word1);
program.step();
@ -51,8 +53,8 @@ public class VM {
break;
case MUL:{
DataWord word1 = program.stackPull();
DataWord word2 = program.stackPull();
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
word1.mull(word2);
program.stackPush(word1);
program.step();
@ -60,8 +62,8 @@ public class VM {
break;
case SUB:{
DataWord word1 = program.stackPull();
DataWord word2 = program.stackPull();
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
word1.sub(word2);
program.stackPush(word1);
program.step();
@ -69,8 +71,8 @@ public class VM {
break;
case DIV:{
DataWord word1 = program.stackPull();
DataWord word2 = program.stackPull();
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
word1.div(word2);
program.stackPush(word1);
program.step();
@ -84,22 +86,22 @@ public class VM {
break;
case EXP:{
DataWord word1 = program.stackPull();
DataWord word2 = program.stackPull();
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
word1.exp(word2);
program.stackPush(word1);
program.step();
}
break;
case NEG:{
DataWord word1 = program.stackPull();
DataWord word1 = program.stackPop();
word1.negate();
program.stackPush(word1);
program.step();
}break;
case LT:{
DataWord word1 = program.stackPull();
DataWord word2 = program.stackPull();
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
if (word1.value().compareTo(word2.value()) == -1){
word1.and(DataWord.ZERO);
word1.getData()[31] = 1;
@ -114,8 +116,8 @@ public class VM {
case SGT:
break;
case GT:{
DataWord word1 = program.stackPull();
DataWord word2 = program.stackPull();
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
if (word1.value().compareTo(word2.value()) == 1){
word1.and(DataWord.ZERO);
word1.getData()[31] = 1;
@ -126,8 +128,8 @@ public class VM {
program.step();
}break;
case EQ:{
DataWord word1 = program.stackPull();
DataWord word2 = program.stackPull();
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
if (word1.xor(word2).isZero()){
word1.and(DataWord.ZERO);
word1.getData()[31] = 1;
@ -139,7 +141,7 @@ public class VM {
}
break;
case NOT: {
DataWord word1 = program.stackPull();
DataWord word1 = program.stackPop();
if (word1.isZero()){
word1.getData()[31] = 1;
} else {
@ -150,31 +152,29 @@ public class VM {
}
break;
/**
* Bitwise Logic Operations
*/
case AND:{
DataWord word1 = program.stackPull();
DataWord word2 = program.stackPull();
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
word1.and(word2);
program.stackPush(word1);
program.step();
}
break;
case OR: {
DataWord word1 = program.stackPull();
DataWord word2 = program.stackPull();
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
word1.or(word2);
program.stackPush(word1);
program.step();
}
break;
case XOR: {
DataWord word1 = program.stackPull();
DataWord word2 = program.stackPull();
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
word1.xor(word2);
program.stackPush(word1);
program.step();
@ -182,8 +182,8 @@ public class VM {
break;
case BYTE:{
DataWord word1 = program.stackPull();
DataWord word2 = program.stackPull();
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
DataWord result = null;
if (word1.value().compareTo(_32_) == -1){
@ -210,26 +210,91 @@ public class VM {
* Environmental Information
*/
case ADDRESS:
break;
case BALANCE:
break;
case ORIGIN:
break;
case CALLER:
break;
case CALLVALUE:
break;
case CALLDATALOAD:
break;
case CALLDATASIZE:
break;
case CALLDATACOPY:
break;
case CODESIZE:
break;
case CODECOPY:
break;
case ADDRESS:{
DataWord address = program.getOwnerAddress();
program.stackPush(address);
program.step();
}
break;
case BALANCE:{
DataWord balance = program.getBalance();
program.stackPush(balance);
program.step();
}
break;
case ORIGIN:{
DataWord originAddress = program.getOriginAddress();
program.stackPush(originAddress);
program.step();
}
break;
case CALLER:{
DataWord callerAddress = program.getCallerAddress();
program.stackPush(callerAddress);
program.step();
}
break;
case CALLVALUE:{
DataWord callValue = program.getCallValue();
program.stackPush(callValue);
program.step();
}
break;
case CALLDATALOAD:{
DataWord dataOffs = program.stackPop();
DataWord value = program.getDataValue(dataOffs);
program.stackPush(value);
program.step();
}
break;
case CALLDATASIZE:{
DataWord dataSize = program.getDataSize();
program.stackPush(dataSize);
program.step();
}
break;
case CALLDATACOPY:{
DataWord memOffsetData = program.stackPop();
DataWord dataOffsetData = program.stackPop();
DataWord lengthData = program.stackPop();
byte[] msgData = program.getDataCopy(dataOffsetData, lengthData);
program.memorySave(memOffsetData.data, msgData);
program.step();
} break;
case CODESIZE:{
DataWord length = new DataWord(program.ops.length);
program.stackPush(length);
program.step();
}
break;
case CODECOPY:{
DataWord memOffsetData = program.stackPop();
DataWord codeOffsetData = program.stackPop();
DataWord lengthData = program.stackPop();
int length = lengthData.value().intValue();
int codeOffset = codeOffsetData.value().intValue();
int memOffset = memOffsetData.value().intValue();
if (program.ops.length < length + codeOffset){
program.stop();
break;
}
byte[] code = new byte[length];
System.arraycopy(program.ops, codeOffset, code, memOffset, length);
program.memorySave(memOffsetData.getData(), code);
program.step();
}
break;
case GASPRICE:
break;
@ -252,12 +317,12 @@ public class VM {
case POP:{
program.stackPull();
program.stackPop();
program.step();
}
break;
case DUP:{
DataWord word_1 = program.stackPull();
DataWord word_1 = program.stackPop();
DataWord word_2 = word_1.clone();
program.stackPush(word_1);
program.stackPush(word_2);
@ -265,8 +330,8 @@ public class VM {
}
break;
case SWAP:{
DataWord word_1 = program.stackPull();
DataWord word_2 = program.stackPull();
DataWord word_1 = program.stackPop();
DataWord word_2 = program.stackPop();
program.stackPush(word_1);
program.stackPush(word_2);
@ -274,15 +339,15 @@ public class VM {
}
break;
case MLOAD:{
DataWord addr = program.stackPull();
DataWord addr = program.stackPop();
DataWord data = program.memoryLoad(addr);
program.stackPush(data);
program.step();
}
break;
case MSTORE:{
DataWord addr = program.stackPull();
DataWord value = program.stackPull();
DataWord addr = program.stackPop();
DataWord value = program.stackPop();
program.memorySave(addr, value);
program.step();
@ -290,15 +355,15 @@ public class VM {
break;
case MSTORE8:{
DataWord addr = program.stackPull();
DataWord value = program.stackPull();
DataWord addr = program.stackPop();
DataWord value = program.stackPop();
byte[] byteVal = {value.getData()[31]};
program.memorySave(addr.getData(), byteVal);
program.step();
}
break;
case SLOAD:{
DataWord key = program.stackPull();
DataWord key = program.stackPop();
DataWord val = program.storageLoad(key);
if (val == null){
@ -309,21 +374,21 @@ public class VM {
}
break;
case SSTORE:{
DataWord addr = program.stackPull();
DataWord value = program.stackPull();
DataWord addr = program.stackPop();
DataWord value = program.stackPop();
program.storageSave(addr, value);
program.step();
}
break;
case JUMP:{
DataWord pos = program.stackPull();
DataWord pos = program.stackPop();
program.setPC(pos);
}
break;
case JUMPI:{
DataWord pos = program.stackPull();
DataWord cond = program.stackPull();
DataWord pos = program.stackPop();
DataWord cond = program.stackPop();
if (!cond.isZero()){
program.setPC(pos);
@ -345,6 +410,7 @@ public class VM {
int memSize = program.getMemSize();
DataWord wordMemSize = new DataWord(memSize);
program.stackPush(wordMemSize);
program.step();
}
break;
case GAS:
@ -394,8 +460,18 @@ public class VM {
break;
case CALL:
break;
case RETURN:
break;
case RETURN:{
DataWord offset = program.stackPop();
DataWord size = program.stackPop();
ByteBuffer hReturn = program.memoryChunk(offset, size);
program.hReturn = hReturn;
program.step();
program.stop();
}
break;
case SUICIDE:
break;
default:

View File

@ -0,0 +1,123 @@
package org.ethereum.vm;
import org.ethereum.crypto.ECKey;
import org.ethereum.crypto.HashUtil;
import org.spongycastle.util.encoders.Hex;
import java.util.List;
/**
* www.ethereumJ.com
* User: Roman Mandeleil
* Created on: 03/06/2014 15:00
*/
public class ProgramInvokeMockImpl implements ProgramInvoke{
byte[] msgData;
public ProgramInvokeMockImpl(byte[] msgDataRaw){
this.msgData = msgDataRaw;
}
ProgramInvokeMockImpl() {
}
/* ADDRESS op */
public DataWord getOwnerAddress(){
byte[] cowPrivKey = HashUtil.sha3("cow".getBytes());
byte[] addr = ECKey.fromPrivate(cowPrivKey).getAddress();
return new DataWord(addr);
}
/* BALANCE op */
public DataWord getBalance(){
byte[] balance = Hex.decode("0DE0B6B3A7640000");
return new DataWord(balance);
}
/* ORIGIN op */
public DataWord getOriginAddress(){
byte[] cowPrivKey = HashUtil.sha3("horse".getBytes());
byte[] addr = ECKey.fromPrivate(cowPrivKey).getAddress();
return new DataWord(addr);
}
/* CALLER op */
public DataWord getCallerAddress(){
byte[] cowPrivKey = HashUtil.sha3("monkey".getBytes());
byte[] addr = ECKey.fromPrivate(cowPrivKey).getAddress();
return new DataWord(addr);
}
/* GASPRICE op */
public DataWord getMinGasPrice(){
byte[] minGasPrice = Hex.decode("09184e72a000");
return new DataWord(minGasPrice);
}
/* CALLVALUE op */
public DataWord getCallValue(){
byte[] balance = Hex.decode("0DE0B6B3A7640000");
return new DataWord(balance);
}
/*****************/
/*** msg data ***/
/*****************/
/* CALLDATALOAD op */
public DataWord getDataValue(DataWord indexData){
byte[] data = new byte[32];
int index = indexData.value().intValue();
int size = 32;
if (msgData == null) return new DataWord(data);
if (index > msgData.length) return new DataWord(data);
if (index + 32 > msgData.length) size = msgData.length - index ;
System.arraycopy(msgData, index, data, 0, size);
return new DataWord(data);
}
/* CALLDATASIZE */
public DataWord getDataSize(){
if (msgData == null || msgData.length == 0) return new DataWord(new byte[32]);
int size = msgData.length;
return new DataWord(size);
}
/* CALLDATACOPY */
public byte[] getDataCopy(DataWord offsetData, DataWord lengthData){
int offset = offsetData.value().intValue();
int length = lengthData.value().intValue();
byte[] data = new byte[length];
if (msgData == null) return data;
if (offset > msgData.length) return data;
if (offset + length > msgData.length) length = msgData.length - offset ;
System.arraycopy(msgData, offset, data, 0, length);
return data;
}
}

File diff suppressed because it is too large Load Diff