Merge pull request #115 from negedzuregal/stateUI
State UI + Account explorer
This commit is contained in:
commit
3fe6f68680
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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<DataClass>());
|
||||
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<DataClass> data;
|
||||
|
||||
final String[] columns = new String[]{ "Account", "Balance", "Is Contract"};
|
||||
|
||||
public AccountsDataAdapter(List<DataClass> 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;
|
||||
}
|
||||
|
||||
}
|
57
ethereumj-studio/src/main/java/org/ethereum/gui/ProgramPlayDialog.java
Normal file → Executable file
57
ethereumj-studio/src/main/java/org/ethereum/gui/ProgramPlayDialog.java
Normal file → Executable file
|
@ -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<String> outputList;
|
||||
private JTextArea console;
|
||||
private JSlider stepSlider;
|
||||
|
||||
|
||||
private ProgramInvoke pi;
|
||||
|
||||
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();
|
||||
this(code, new ProgramInvokeMockImpl(), null);
|
||||
}
|
||||
|
||||
|
||||
public ProgramPlayDialog(byte[] code, Transaction tx, Block lastBlock) {
|
||||
|
||||
outputList = new ArrayList<String>();
|
||||
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<String>();
|
||||
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) {
|
||||
|
|
|
@ -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<DataWord, DataWord> 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<DataWord, DataWord> data;
|
||||
DataEncodingType keyEncodingType = DataEncodingType.HEX;
|
||||
DataEncodingType valueEncodingType = DataEncodingType.HEX;
|
||||
String[] columns = new String[]{ "Key", "Value"};
|
||||
|
||||
public StateDataTableModel() { }
|
||||
|
||||
public StateDataTableModel(Map<DataWord, DataWord> initData) {
|
||||
setData(initData);
|
||||
}
|
||||
|
||||
public void setData(Map<DataWord, DataWord> 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 415 B |
Binary file not shown.
After Width: | Height: | Size: 927 B |
Loading…
Reference in New Issue