diff --git a/ethereumj-core/src/main/java/org/ethereum/core/AccountState.java b/ethereumj-core/src/main/java/org/ethereum/core/AccountState.java index dde54985..d838d4e0 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/AccountState.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/AccountState.java @@ -2,8 +2,11 @@ package org.ethereum.core; import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY; import static org.ethereum.crypto.HashUtil.EMPTY_DATA_HASH; + import org.ethereum.util.RLP; import org.ethereum.util.RLPList; +import org.spongycastle.util.Arrays; +import org.spongycastle.util.encoders.Hex; import java.math.BigInteger; @@ -113,4 +116,16 @@ public class AccountState { } return rlpEncoded; } + + public String toString() { + String ret = "Nonce: " + this.getNonce().toString() + "\n" + + "Balance: " + Denomination.toFriendlyString(getBalance()) + "\n"; + + if(this.getStateRoot()!= null && !Arrays.areEqual(this.getStateRoot(), EMPTY_BYTE_ARRAY)) + ret += "State Root: " + Hex.toHexString(this.getStateRoot()) + "\n"; + if(this.getCodeHash() != null && !Arrays.areEqual(this.getCodeHash(), EMPTY_DATA_HASH)) + ret += "Code Hash: " + Hex.toHexString(this.getCodeHash()); + + return ret; + } } diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Denomination.java b/ethereumj-core/src/main/java/org/ethereum/core/Denomination.java index 9a4af508..7bb04247 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Denomination.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Denomination.java @@ -31,4 +31,33 @@ public enum Denomination { private static BigInteger newBigInt(int value) { return BigInteger.valueOf(10).pow(value); } + + public static String toFriendlyString(BigInteger value) { + if(value.compareTo(DOUGLAS.value()) == 1 || value.compareTo(DOUGLAS.value()) == 0) { + return Float.toString(value.divide(DOUGLAS.value()).floatValue()) + " DOUGLAS"; + } + else if(value.compareTo(EINSTEIN.value()) == 1 || value.compareTo(EINSTEIN.value()) == 0) { + return Float.toString(value.divide(EINSTEIN.value()).floatValue()) + " EINSTEIN"; + } + else if(value.compareTo(ETHER.value()) == 1 || value.compareTo(ETHER.value()) == 0) { + return Float.toString(value.divide(ETHER.value()).floatValue()) + " ETHER"; + } + else if(value.compareTo(FINNY.value()) == 1 || value.compareTo(FINNY.value()) == 0) { + return Float.toString(value.divide(FINNY.value()).floatValue()) + " FINNY"; + } + else if(value.compareTo(SZABO.value()) == 1 || value.compareTo(SZABO.value()) == 0) { + return Float.toString(value.divide(SZABO.value()).floatValue()) + " SZABO"; + } + else if(value.compareTo(SHANNON.value()) == 1 || value.compareTo(SHANNON.value()) == 0) { + return Float.toString(value.divide(SHANNON.value()).floatValue()) + " SHANNON"; + } + else if(value.compareTo(BABBAGE.value()) == 1 || value.compareTo(BABBAGE.value()) == 0) { + return Float.toString(value.divide(BABBAGE.value()).floatValue()) + " BABBAGE"; + } + else if(value.compareTo(ADA.value()) == 1 || value.compareTo(ADA.value()) == 0) { + return Float.toString(value.divide(ADA.value()).floatValue()) + " ADA"; + } + else + return Float.toString(value.divide(WEI.value()).floatValue()) + " WEI"; + } } diff --git a/ethereumj-core/src/main/java/org/ethereum/db/RepositoryImpl.java b/ethereumj-core/src/main/java/org/ethereum/db/RepositoryImpl.java index 1bcad566..60a3ee18 100644 --- a/ethereumj-core/src/main/java/org/ethereum/db/RepositoryImpl.java +++ b/ethereumj-core/src/main/java/org/ethereum/db/RepositoryImpl.java @@ -75,10 +75,14 @@ public class RepositoryImpl implements Repository { * @See loadBlockchain() to update the stateRoot */ public RepositoryImpl() { - chainDB = new DatabaseImpl("blockchain"); - detailsDB = new DatabaseImpl("details"); + this("blockchain", "details", "state"); + } + + public RepositoryImpl(String blockChainDbName, String detailsDbName, String stateDbName) { + chainDB = new DatabaseImpl(blockChainDbName); + detailsDB = new DatabaseImpl(detailsDbName); contractDetailsDB = new TrackDatabase(detailsDB); - stateDB = new DatabaseImpl("state"); + stateDB = new DatabaseImpl(stateDbName); worldState = new Trie(stateDB.getDb()); accountStateDB = new TrackTrie(worldState); } @@ -479,6 +483,9 @@ public class RepositoryImpl implements Repository { } } + public DBIterator getContractDetailsDBIterator() { + return detailsDB.iterator(); + } public boolean isClosed(){ return chainDB == null; diff --git a/ethereumj-core/src/main/java/org/ethereum/vm/Program.java b/ethereumj-core/src/main/java/org/ethereum/vm/Program.java index a7871b98..9685e471 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/Program.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/Program.java @@ -695,6 +695,37 @@ public class Program { } } } + + public static String stringify(byte[] code, int index, String result) { + if(code == null || code.length == 0) + return result; + + OpCode op = OpCode.code(code[index]); + byte[] continuedCode = null; + + switch(op){ + 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: + result += ' ' + op.name() + ' '; + + int nPush = op.val() - OpCode.PUSH1.val() + 1; + byte[] data = Arrays.copyOfRange(code, index+1, index + nPush + 1); + result += new BigInteger(data).toString() + ' '; + + continuedCode = Arrays.copyOfRange(code, index + nPush + 1, code.length); + break; + + default: + result += ' ' + op.name(); + continuedCode = Arrays.copyOfRange(code, index + 1, code.length); + break; + + } + + return stringify(continuedCode, 0, result); + } public void addListener(ProgramListener listener) { this.listener = listener; diff --git a/ethereumj-core/src/main/java/org/ethereum/vm/ProgramInvokeMockImpl.java b/ethereumj-core/src/main/java/org/ethereum/vm/ProgramInvokeMockImpl.java index d35cfcb5..8f6b0749 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/ProgramInvokeMockImpl.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/ProgramInvokeMockImpl.java @@ -24,7 +24,7 @@ public class ProgramInvokeMockImpl implements ProgramInvoke { } public ProgramInvokeMockImpl() { - this.repository = new RepositoryImpl(); + this.repository = new RepositoryImpl("blockchainMoc", "detailsMoc", "stateMoc"); this.repository.createAccount(Hex.decode(ownerAddress)); } diff --git a/ethereumj-studio/src/main/java/org/ethereum/gui/AccountsListWindow.java b/ethereumj-studio/src/main/java/org/ethereum/gui/AccountsListWindow.java new file mode 100755 index 00000000..e292fa55 --- /dev/null +++ b/ethereumj-studio/src/main/java/org/ethereum/gui/AccountsListWindow.java @@ -0,0 +1,135 @@ +package org.ethereum.gui; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Image; +import java.awt.Toolkit; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.table.AbstractTableModel; + +import org.ethereum.core.Account; +import org.ethereum.core.AccountState; +import org.ethereum.core.Denomination; +import org.ethereum.crypto.HashUtil; +import org.ethereum.manager.WorldManager; +import org.iq80.leveldb.DBIterator; +import org.spongycastle.util.Arrays; +import org.spongycastle.util.encoders.Hex; + +public class AccountsListWindow extends JFrame { + + private JTable tblAccountsDataTable; + private AccountsDataAdapter adapter; + + public AccountsListWindow() { + java.net.URL url = ClassLoader.getSystemResource("ethereum-icon.png"); + Toolkit kit = Toolkit.getDefaultToolkit(); + Image img = kit.createImage(url); + this.setIconImage(img); + setTitle("Accounts List"); + setSize(700, 500); + setLocation(50, 180); + setResizable(false); + + JPanel panel = new JPanel(); + getContentPane().add(panel); + + tblAccountsDataTable = new JTable(); + + adapter = new AccountsDataAdapter(new ArrayList()); + tblAccountsDataTable.setModel(adapter); + + JScrollPane scrollPane = new JScrollPane(tblAccountsDataTable); + scrollPane.setPreferredSize(new Dimension(680,490)); + panel.add(scrollPane); + + loadAccounts(); + } + + private void loadAccounts() { + new Thread(){ + + @Override + public void run(){ + DBIterator i = WorldManager.getInstance().getRepository().getContractDetailsDBIterator(); + while(i.hasNext()) { + DataClass dc = new DataClass(); + dc.address = i.next().getKey(); + + AccountState state = WorldManager.getInstance().getRepository().getAccountState(dc.address); + dc.accountState = state; + + adapter.addDataPiece(dc); + } + } + }.start(); + } + + private class AccountsDataAdapter extends AbstractTableModel { + List data; + + final String[] columns = new String[]{ "Account", "Balance", "Is Contract"}; + + public AccountsDataAdapter(List data) { + this.data = data; + } + + public void addDataPiece(DataClass d) { + data.add(d); + this.fireTableRowsInserted(Math.min(data.size() - 2, 0), data.size() - 1); + } + + @Override + public int getRowCount() { + return data.size(); + } + + @Override + public int getColumnCount() { + return 3; + } + + @Override + public String getColumnName(int column) { + return columns[column]; + } + + @Override + public boolean isCellEditable(int row, int column) { // custom isCellEditable function + return column == 0? true:false; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + if(columnIndex == 0) { + return Hex.toHexString(data.get(rowIndex).address); + } + else if(columnIndex == 1 ){ + if(data.get(rowIndex).accountState != null) { + return Denomination.toFriendlyString(data.get(rowIndex).accountState.getBalance()); + } + return "---"; + } + else { + if(data.get(rowIndex).accountState != null) { + if(!Arrays.areEqual(data.get(rowIndex).accountState.getCodeHash(), HashUtil.EMPTY_DATA_HASH)) + return "Yes"; + } + return "No"; + } + } + } + + private class DataClass { + public byte[] address; + public AccountState accountState; + } + +} diff --git a/ethereumj-studio/src/main/java/org/ethereum/gui/ProgramPlayDialog.java b/ethereumj-studio/src/main/java/org/ethereum/gui/ProgramPlayDialog.java old mode 100644 new mode 100755 index c41aa3c6..6b024eed --- a/ethereumj-studio/src/main/java/org/ethereum/gui/ProgramPlayDialog.java +++ b/ethereumj-studio/src/main/java/org/ethereum/gui/ProgramPlayDialog.java @@ -3,6 +3,7 @@ package org.ethereum.gui; import org.ethereum.core.Block; import org.ethereum.core.Transaction; import org.ethereum.db.RepositoryImpl; +import org.ethereum.facade.Repository; import org.ethereum.manager.WorldManager; import org.ethereum.vm.*; import org.spongycastle.util.encoders.Hex; @@ -10,6 +11,7 @@ import org.spongycastle.util.encoders.Hex; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; + import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -28,42 +30,40 @@ public class ProgramPlayDialog extends JPanel implements ActionListener, private List outputList; private JTextArea console; private JSlider stepSlider; - + + private ProgramInvoke pi; + public ProgramPlayDialog(byte[] code) { - - outputList = new ArrayList(); - VM vm = new VM(); - - ProgramInvoke pi = new ProgramInvokeMockImpl(); - Program program = new Program(code, pi); - - program.addListener(this); - program.fullTrace(); - vm.play(program); - - doGUI(); + this(code, new ProgramInvokeMockImpl(), null); } - + public ProgramPlayDialog(byte[] code, Transaction tx, Block lastBlock) { - - outputList = new ArrayList(); + this(code, + ProgramInvokeFactory.createProgramInvoke(tx, + lastBlock, + WorldManager.getInstance().getRepository()), + WorldManager.getInstance().getRepository()); + } + + public ProgramPlayDialog(byte[] code, ProgramInvoke programInvoke, RepositoryImpl tractRepository) { + pi = programInvoke; + + outputList = new ArrayList(); VM vm = new VM(); - RepositoryImpl tractRepository = WorldManager.getInstance().getRepository().getTrack(); - - Program program = new Program(code , - ProgramInvokeFactory.createProgramInvoke(tx, lastBlock, tractRepository)); - + Program program = new Program(code, programInvoke); program.addListener(this); program.fullTrace(); vm.play(program); - tractRepository.rollback(); + if(tractRepository != null) + tractRepository.rollback(); doGUI(); } - + public void doGUI() { + setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); //Create the slider. @@ -136,7 +136,7 @@ public class ProgramPlayDialog extends JPanel implements ActionListener, */ public static void createAndShowGUI(byte[] runCode, Transaction tx, Block lastBlock) { - ProgramPlayDialog ppd; + final ProgramPlayDialog ppd; if (tx != null) ppd = new ProgramPlayDialog(runCode, tx, lastBlock); else{ @@ -157,12 +157,21 @@ public class ProgramPlayDialog extends JPanel implements ActionListener, //Add content to the window. frame.add(ppd, BorderLayout.CENTER); + + // close event + frame.addWindowListener(new java.awt.event.WindowAdapter() { + @Override + public void windowClosing(java.awt.event.WindowEvent windowEvent) { + ppd.pi.getRepository().close(); + } + }); //Display the window. frame.pack(); frame.setVisible(true); ppd.setFocus(); } + @Override public void output(String out) { diff --git a/ethereumj-studio/src/main/java/org/ethereum/gui/StateExplorerWindow.java b/ethereumj-studio/src/main/java/org/ethereum/gui/StateExplorerWindow.java new file mode 100755 index 00000000..759429ff --- /dev/null +++ b/ethereumj-studio/src/main/java/org/ethereum/gui/StateExplorerWindow.java @@ -0,0 +1,353 @@ +package org.ethereum.gui; + +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Image; +import java.awt.TextArea; +import java.awt.Toolkit; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.math.BigInteger; +import java.util.Map; + +import javax.swing.Box; +import javax.swing.ImageIcon; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.JTextField; +import javax.swing.JButton; +import javax.swing.JToggleButton; +import javax.swing.SwingUtilities; +import javax.swing.table.AbstractTableModel; + +import org.ethereum.core.AccountState; +import org.ethereum.core.Block; +import org.ethereum.core.Transaction; +import org.ethereum.db.ContractDetails; +import org.ethereum.manager.WorldManager; +import org.ethereum.vm.DataWord; +import org.ethereum.vm.OpCode; +import org.ethereum.vm.Program; +import org.ethereum.vm.ProgramInvoke; +import org.ethereum.vm.ProgramInvokeFactory; +import org.ethereum.vm.Program.ProgramListener; +import org.spongycastle.util.encoders.DecoderException; +import org.spongycastle.util.encoders.Hex; +import java.awt.Component; +import java.awt.FlowLayout; + +public class StateExplorerWindow extends JFrame{ + + private ToolBar toolBar = null; + private JTextField txfAccountAddress; + private WindowTextArea txaPrinter; + private JButton btnPlayCode; + private AccountsListWindow accountsListWindow; + ProgramPlayDialog codePanel; + + private JTable tblStateDataTable; + private StateDataTableModel dataModel; + String[] dataTypes = {"String", "Hex", "Number"}; + + public StateExplorerWindow(ToolBar toolBar) { + this.toolBar = toolBar; + + java.net.URL url = ClassLoader.getSystemResource("ethereum-icon.png"); + Toolkit kit = Toolkit.getDefaultToolkit(); + Image img = kit.createImage(url); + this.setIconImage(img); + setTitle("State Explorer"); + setSize(700, 530); + setLocation(50, 180); + setResizable(false); + + /* + * top search panel + */ + JPanel panel = new JPanel(); + getContentPane().add(panel); + + Box horizontalBox = Box.createHorizontalBox(); + panel.add(horizontalBox); + + java.net.URL imageURL = ClassLoader.getSystemResource("buttons/list.png"); + ImageIcon image = new ImageIcon(imageURL); + JToggleButton btnListAccounts = new JToggleButton(""); + btnListAccounts.setIcon(image); + btnListAccounts.setContentAreaFilled(true); + btnListAccounts.setToolTipText("Serpent Editor"); + btnListAccounts.setBackground(Color.WHITE); + btnListAccounts.setBorderPainted(false); + btnListAccounts.setFocusPainted(false); + btnListAccounts.setCursor(new Cursor(Cursor.HAND_CURSOR)); + btnListAccounts.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if(accountsListWindow == null) + accountsListWindow = new AccountsListWindow(); + accountsListWindow.setVisible(true); + } + }); + } + }); + horizontalBox.add(btnListAccounts); + + + txfAccountAddress = new JTextField(); + horizontalBox.add(txfAccountAddress); + txfAccountAddress.setColumns(30); + + JButton btnSearch = new JButton("Search"); + horizontalBox.add(btnSearch); + btnSearch.addMouseListener(new MouseAdapter(){ + @Override + public void mouseClicked(MouseEvent e) { + searchAccount(txfAccountAddress.getText()); + } + }); + + btnPlayCode = new JButton("Play Code"); + horizontalBox.add(btnPlayCode); + btnPlayCode.addMouseListener(new MouseAdapter(){ + @Override + public void mouseClicked(MouseEvent e) { + byte[] code = WorldManager.getInstance().getRepository().getCode(Hex.decode(txfAccountAddress.getText())); + if(code != null) + ProgramPlayDialog.createAndShowGUI(code, null, null); + } + }); + + /* + * center text panel + */ + JPanel centerPanel = new JPanel(); + panel.add(centerPanel); + + txaPrinter = new WindowTextArea(); + centerPanel.add(txaPrinter); + + /* + * bottom data panel + */ + // data type choice boxes + Box Hbox = Box.createHorizontalBox(); + panel.add(Hbox); + + Box VBox1 = Box.createVerticalBox(); + JLabel l1 = new JLabel("Key Encoding"); + l1.setAlignmentX(Component.CENTER_ALIGNMENT); + JComboBox cmbKey = new JComboBox(dataTypes); + cmbKey.setSelectedIndex(1); + cmbKey.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JComboBox cmb = (JComboBox) e.getSource(); + DataEncodingType t = DataEncodingType.getTypeFromString((String) cmb.getSelectedItem()); + dataModel.setKeyEncoding(t); + } + }); + VBox1.add(l1); + VBox1.add(cmbKey); + + Box VBox2 = Box.createVerticalBox(); + VBox2.setAlignmentX(LEFT_ALIGNMENT); + JLabel l2 = new JLabel("Value Encoding"); + l2.setAlignmentX(Component.CENTER_ALIGNMENT); + JComboBox cmbValue = new JComboBox(dataTypes); + cmbValue.setSelectedIndex(1); + cmbValue.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JComboBox cmb = (JComboBox) e.getSource(); + DataEncodingType t = DataEncodingType.getTypeFromString((String) cmb.getSelectedItem()); + dataModel.setValueEncoding(t); + } + }); + VBox2.add(l2); + VBox2.add(cmbValue); + + Hbox.add(VBox1); + + JPanel spacer = new JPanel(); + FlowLayout flowLayout = (FlowLayout) spacer.getLayout(); + flowLayout.setHgap(100); + Hbox.add(spacer); + Hbox.add(VBox2); + + // table + tblStateDataTable = new JTable(); + dataModel = new StateDataTableModel(); + tblStateDataTable.setModel(dataModel); + + + JScrollPane scrollPane = new JScrollPane(tblStateDataTable); + scrollPane.setPreferredSize(new Dimension(600,200)); + panel.add(scrollPane); + } + + private void searchAccount(String accountStr){ + txaPrinter.clean(); + + byte[] add = null; + try { add = Hex.decode(txfAccountAddress.getText()); } + catch(DecoderException ex) { return; } + + txaPrinter.println(accountDetailsString(add, dataModel)); + } + + private String accountDetailsString(byte[] account, StateDataTableModel dataModel){ + String ret = ""; + // 1) print account address + ret = "Account: " + Hex.toHexString(account) + "\n"; + + //2) print state + AccountState state = WorldManager.getInstance().getRepository().getAccountState(account); + if(state != null) + ret += state.toString() + "\n"; + + //3) print storage + ContractDetails contractDetails = WorldManager.getInstance().getRepository().getContractDetails(account); + if(contractDetails != null) { + Map accountStorage = contractDetails.getStorage(); + dataModel.setData(accountStorage); + } + + //4) code print + byte[] code = WorldManager.getInstance().getRepository().getCode(account); + if(code != null) { + ret += "\n\nCode:\n"; + ret += Program.stringify(code, 0, ""); + } + + return ret; + } + + private class StateDataTableModel extends AbstractTableModel { + + Map data; + DataEncodingType keyEncodingType = DataEncodingType.HEX; + DataEncodingType valueEncodingType = DataEncodingType.HEX; + String[] columns = new String[]{ "Key", "Value"}; + + public StateDataTableModel() { } + + public StateDataTableModel(Map initData) { + setData(initData); + } + + public void setData(Map initData) { + data = initData; + fireTableDataChanged(); + } + + public void setKeyEncoding(DataEncodingType type) { + keyEncodingType = type; + fireTableDataChanged(); + } + + public void setValueEncoding(DataEncodingType type) { + valueEncodingType = type; + fireTableDataChanged(); + } + + @Override + public String getColumnName(int column) { + return columns[column]; + } + + @Override + public int getRowCount() { + return data == null? 0:data.size(); + } + + @Override + public int getColumnCount() { + return columns.length; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + DataWord key = (DataWord) this.data.keySet().toArray()[rowIndex]; + + if(columnIndex == 0) { + return getDataWithEncoding(key.getData(), keyEncodingType); + } + else { + DataWord value = this.data.get(key); + return getDataWithEncoding(value.getData(), valueEncodingType); + } + } + + private String getDataWithEncoding(byte[] data, DataEncodingType enc) { + switch(enc) { + case STRING: + return new String(data); + case HEX: + return Hex.toHexString(data); + case NUMBER: + return new BigInteger(data).toString(); + } + + return data.toString(); + } + } + + private enum DataEncodingType{ + STRING, + HEX, + NUMBER; + + static public DataEncodingType getTypeFromString(String value) { + switch(value){ + case "String": + return STRING; + case "Hex": + return HEX; + case "Number": + return NUMBER; + } + return STRING; + } + } + + private class WindowTextArea extends TextArea { + + public WindowTextArea() { + super(); + } + + public void println(String txt) { + setText(getText() + txt + "\n"); + } + + public void clean() { + setText(""); + } + } + + public static void main(String[] args) { + // Start all Swing applications on the EDT. + SwingUtilities.invokeLater(new Runnable() { + public void run() { + new StateExplorerWindow(null).setVisible(true); + } + }); + } +} diff --git a/ethereumj-studio/src/main/java/org/ethereum/gui/ToolBar.java b/ethereumj-studio/src/main/java/org/ethereum/gui/ToolBar.java index ce1bdf91..53929e94 100644 --- a/ethereumj-studio/src/main/java/org/ethereum/gui/ToolBar.java +++ b/ethereumj-studio/src/main/java/org/ethereum/gui/ToolBar.java @@ -28,12 +28,14 @@ public class ToolBar extends JFrame { private BlockChainTable blockchainWindow = null; private WalletWindow walletWindow = null; private SerpentEditor serpentEditor = null; + private StateExplorerWindow stateExplorerWindow = null; JToggleButton editorToggle; JToggleButton logToggle; JToggleButton peersToggle; JToggleButton chainToggle; JToggleButton walletToggle; + JToggleButton stateExplorer; public ToolBar() throws HeadlessException { @@ -94,6 +96,9 @@ public class ToolBar extends JFrame { java.net.URL imageURL_5 = ClassLoader.getSystemResource("buttons/wallet.png"); ImageIcon image_5 = new ImageIcon(imageURL_5); + + java.net.URL imageURL_6 = ClassLoader.getSystemResource("buttons/stateExplorer.png"); + ImageIcon image_6 = new ImageIcon(imageURL_6); editorToggle = new JToggleButton(""); editorToggle.setIcon(image_1); @@ -222,12 +227,41 @@ public class ToolBar extends JFrame { } } ); + + stateExplorer = new JToggleButton(); + stateExplorer.setIcon(image_6); + stateExplorer.setToolTipText("State Explorer"); + stateExplorer.setContentAreaFilled(true); + stateExplorer.setBackground(Color.WHITE); + stateExplorer.setBorderPainted(false); + stateExplorer.setFocusPainted(false); + stateExplorer.setCursor(new Cursor(Cursor.HAND_CURSOR)); + stateExplorer.addItemListener( + new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (stateExplorerWindow == null) + stateExplorerWindow = new StateExplorerWindow(ToolBar.this); + stateExplorerWindow.setVisible(true); + } + }); + } else if (e.getStateChange() == ItemEvent.DESELECTED) { + stateExplorerWindow.setVisible(false); + } + } + } + ); + cp.add(editorToggle); cp.add(logToggle); cp.add(peersToggle); cp.add(chainToggle); cp.add(walletToggle); + cp.add(stateExplorer); Ethereum ethereum = UIEthereumManager.ethereum; diff --git a/ethereumj-studio/src/main/resources/buttons/list.png b/ethereumj-studio/src/main/resources/buttons/list.png new file mode 100755 index 00000000..7adc45ea Binary files /dev/null and b/ethereumj-studio/src/main/resources/buttons/list.png differ diff --git a/ethereumj-studio/src/main/resources/buttons/stateExplorer.png b/ethereumj-studio/src/main/resources/buttons/stateExplorer.png new file mode 100755 index 00000000..334b0f01 Binary files /dev/null and b/ethereumj-studio/src/main/resources/buttons/stateExplorer.png differ