From e7b2fd7d19878ef3b9f72919c0a3c692279082e2 Mon Sep 17 00:00:00 2001 From: romanman Date: Sat, 17 May 2014 15:18:49 +0300 Subject: [PATCH] Ethereum Wallet Prototype Added GUI for future wallet, to run it one can run ToolBar and click on the wallet icon. --- .../main/java/org/ethereum/core/Block.java | 30 ++++++++- ...sole.java => ConnectionConsoleWindow.java} | 8 +-- ...rsTableMain.java => PeersTableWindow.java} | 6 +- .../main/java/org/ethereum/gui/ToolBar.java | 11 ++- .../org/ethereum/gui/WalletAddressPanel.java | 61 +++++++++++++++++ .../java/org/ethereum/gui/WalletSumPanel.java | 57 ++++++++++++++++ .../java/org/ethereum/gui/WalletWindow.java | 63 ++++++++++++++++++ .../ethereum/net/message/BlocksMessage.java | 6 +- .../main/resources/buttons/add-address.png | Bin 0 -> 3287 bytes .../src/main/resources/buttons/wallet-pay.png | Bin 0 -> 3848 bytes 10 files changed, 227 insertions(+), 15 deletions(-) rename ethereumj-core/src/main/java/org/ethereum/gui/{ConnectionConsole.java => ConnectionConsoleWindow.java} (93%) rename ethereumj-core/src/main/java/org/ethereum/gui/{PeersTableMain.java => PeersTableWindow.java} (94%) create mode 100644 ethereumj-core/src/main/java/org/ethereum/gui/WalletAddressPanel.java create mode 100644 ethereumj-core/src/main/java/org/ethereum/gui/WalletSumPanel.java create mode 100644 ethereumj-core/src/main/java/org/ethereum/gui/WalletWindow.java create mode 100644 ethereumj-core/src/main/resources/buttons/add-address.png create mode 100644 ethereumj-core/src/main/resources/buttons/wallet-pay.png diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Block.java b/ethereumj-core/src/main/java/org/ethereum/core/Block.java index 41b4a45a..c8fcd397 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Block.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Block.java @@ -244,6 +244,7 @@ public class Block { // [parent_hash, uncles_hash, coinbase, state_root, tx_trie_root, // difficulty, number, minGasPrice, gasLimit, gasUsed, timestamp, // extradata, nonce] + @Override public String toString() { if (!parsed) parseRLP(); @@ -271,10 +272,37 @@ public class Block { toStringBuff.append( tx.toString() ); } - toStringBuff.append("\n]"); + return toStringBuff.toString(); + } + public String toFlatString(){ + if (!parsed) parseRLP(); + toStringBuff.setLength(0); + toStringBuff.append("BlockData ["); + toStringBuff.append(" hash=" + Utils.toHexString(hash)).append(""); + toStringBuff.append(" parentHash=" + Utils.toHexString(parentHash)).append(""); + toStringBuff.append(" unclesHash=" + Utils.toHexString(unclesHash)).append(""); + toStringBuff.append(" coinbase=" + Utils.toHexString(coinbase)).append(""); + toStringBuff.append(" stateHash=" + Utils.toHexString(stateRoot)).append(""); + toStringBuff.append(" txTrieHash=" + Utils.toHexString(txTrieRoot)).append(""); + toStringBuff.append(" difficulty=" + Utils.toHexString(difficulty)).append(""); + toStringBuff.append(" number=" + number).append(""); + toStringBuff.append(" minGasPrice=" + minGasPrice).append(""); + toStringBuff.append(" gasLimit=" + gasLimit).append(""); + toStringBuff.append(" gasUsed=" + gasUsed).append(""); + toStringBuff.append(" timestamp=" + timestamp).append(""); + toStringBuff.append(" extraData=" + Utils.toHexString(extraData)).append(""); + toStringBuff.append(" nonce=" + Utils.toHexString(nonce)).append(""); + + for (Transaction tx : getTransactionsList()){ + + toStringBuff.append("\n"); + toStringBuff.append( tx.toString() ); + } + + toStringBuff.append("]"); return toStringBuff.toString(); } diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/ConnectionConsole.java b/ethereumj-core/src/main/java/org/ethereum/gui/ConnectionConsoleWindow.java similarity index 93% rename from ethereumj-core/src/main/java/org/ethereum/gui/ConnectionConsole.java rename to ethereumj-core/src/main/java/org/ethereum/gui/ConnectionConsoleWindow.java index 209b8c1b..fbeff685 100644 --- a/ethereumj-core/src/main/java/org/ethereum/gui/ConnectionConsole.java +++ b/ethereumj-core/src/main/java/org/ethereum/gui/ConnectionConsoleWindow.java @@ -26,7 +26,7 @@ import org.fife.ui.rtextarea.RTextScrollPane; * Project Home: http://fifesoft.com/rsyntaxtextarea
* Downloads: https://sourceforge.net/projects/rsyntaxtextarea */ -public class ConnectionConsole extends JFrame implements PeerListener{ +public class ConnectionConsoleWindow extends JFrame implements PeerListener{ private static final long serialVersionUID = 1L; @@ -39,8 +39,8 @@ public class ConnectionConsole extends JFrame implements PeerListener{ * TRACE (start/end method) */ - public ConnectionConsole() { - final ConnectionConsole thisConsole = this; + public ConnectionConsoleWindow() { + final ConnectionConsoleWindow thisConsole = this; java.net.URL url = ClassLoader.getSystemResource("ethereum-icon.png"); Toolkit kit = Toolkit.getDefaultToolkit(); @@ -95,7 +95,7 @@ public class ConnectionConsole extends JFrame implements PeerListener{ // Start all Swing applications on the EDT. SwingUtilities.invokeLater(new Runnable() { public void run() { - new ConnectionConsole().setVisible(true); + new ConnectionConsoleWindow().setVisible(true); } }); } diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/PeersTableMain.java b/ethereumj-core/src/main/java/org/ethereum/gui/PeersTableWindow.java similarity index 94% rename from ethereumj-core/src/main/java/org/ethereum/gui/PeersTableMain.java rename to ethereumj-core/src/main/java/org/ethereum/gui/PeersTableWindow.java index 367d7579..169f08cd 100644 --- a/ethereumj-core/src/main/java/org/ethereum/gui/PeersTableMain.java +++ b/ethereumj-core/src/main/java/org/ethereum/gui/PeersTableWindow.java @@ -20,7 +20,7 @@ import javax.swing.table.TableCellRenderer; * User: Roman Mandeleil * Created on: 25/04/14 07:11 */ -public class PeersTableMain extends JFrame{ +public class PeersTableWindow extends JFrame{ // Instance attributes used in this example private JPanel topPanel; @@ -28,7 +28,7 @@ public class PeersTableMain extends JFrame{ private JScrollPane scrollPane; // Constructor of main frame - public PeersTableMain() { + public PeersTableWindow() { // Set the frame characteristics setTitle("Ethereum Peers"); setSize(355, 300); @@ -75,7 +75,7 @@ public class PeersTableMain extends JFrame{ public static void main(String args[]) { - PeersTableMain mainFrame = new PeersTableMain(); + PeersTableWindow mainFrame = new PeersTableWindow(); mainFrame.setVisible(true); mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/ToolBar.java b/ethereumj-core/src/main/java/org/ethereum/gui/ToolBar.java index 1bbe2b05..95055393 100644 --- a/ethereumj-core/src/main/java/org/ethereum/gui/ToolBar.java +++ b/ethereumj-core/src/main/java/org/ethereum/gui/ToolBar.java @@ -80,7 +80,7 @@ public class ToolBar extends JFrame { public void actionPerformed(ActionEvent e) { SwingUtilities.invokeLater(new Runnable() { public void run() { - new ConnectionConsole().setVisible(true); + new ConnectionConsoleWindow().setVisible(true); } }); } @@ -97,7 +97,7 @@ public class ToolBar extends JFrame { peersToggle.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - PeersTableMain mainFrame = new PeersTableMain(); + PeersTableWindow mainFrame = new PeersTableWindow(); mainFrame.setVisible( true ); // mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } @@ -128,6 +128,13 @@ public class ToolBar extends JFrame { walletToggle.setBorderPainted(false); walletToggle.setFocusPainted(false); walletToggle.setCursor(new Cursor(Cursor.HAND_CURSOR)); + walletToggle.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + WalletWindow walletWindow = new WalletWindow(); + walletWindow.setVisible(true); + } + }); cp.add(editorToggle); cp.add(logToggle); diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/WalletAddressPanel.java b/ethereumj-core/src/main/java/org/ethereum/gui/WalletAddressPanel.java new file mode 100644 index 00000000..6d279d38 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/gui/WalletAddressPanel.java @@ -0,0 +1,61 @@ +package org.ethereum.gui; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.net.URL; + +/** + * www.ethereumJ.com + * User: Roman Mandeleil + * Created on: 17/05/14 12:32 + */ +public class WalletAddressPanel extends JPanel{ + + public WalletAddressPanel() { + + this.setBackground(Color.WHITE); + double width = this.getSize().getWidth(); + this.setPreferredSize(new Dimension(500, 50)); + + JTextField addressField = new JTextField(); + Border line = BorderFactory.createLineBorder(Color.LIGHT_GRAY); + Border empty = new EmptyBorder(5, 8, 5, 8); + CompoundBorder border = new CompoundBorder(line, empty); + addressField.setBorder(border); + addressField.setEnabled(true); + addressField.setEditable(false); + addressField.setText("5a554ee950faddf206972771bebd3dc0f13f1f4d"); + addressField.setForeground(new Color(143, 170, 220)); + addressField.setBackground(Color.WHITE); + this.add(addressField); + + JTextField amount = new JTextField(); + amount.setBorder(border); + amount.setEnabled(true); + amount.setEditable(false); + amount.setText("234 * 10^9"); + amount.setForeground(new Color(143, 170, 220)); + amount.setBackground(Color.WHITE); + this.add(amount); + + URL payoutIconURL = ClassLoader.getSystemResource("buttons/wallet-pay.png"); + ImageIcon payOutIcon = new ImageIcon(payoutIconURL); + JLabel payOutLabel = new JLabel(payOutIcon); + payOutLabel.setToolTipText("Payout for address"); + payOutLabel.setCursor(new Cursor(Cursor.HAND_CURSOR)); + payOutLabel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + System.out.println("boom"); + } + }); + + + this.add(payOutLabel); + } +} diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/WalletSumPanel.java b/ethereumj-core/src/main/java/org/ethereum/gui/WalletSumPanel.java new file mode 100644 index 00000000..ff949d18 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/gui/WalletSumPanel.java @@ -0,0 +1,57 @@ +package org.ethereum.gui; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.net.URL; + +/** + * www.ethereumJ.com + * User: Roman Mandeleil + * Created on: 17/05/14 12:32 + */ +public class WalletSumPanel extends JPanel{ + + public WalletSumPanel() { + + this.setBackground(Color.WHITE); + double width = this.getSize().getWidth(); + this.setPreferredSize(new Dimension(500, 50)); + Border line = BorderFactory.createLineBorder(Color.LIGHT_GRAY); + Border empty = new EmptyBorder(5, 8, 5, 8); + CompoundBorder border = new CompoundBorder(line, empty); + + JLabel addressField = new JLabel(); + addressField.setPreferredSize(new Dimension(276, 50)); + this.add(addressField); + + JTextField amount = new JTextField(); + amount.setBorder(border); + amount.setEnabled(true); + amount.setEditable(false); + amount.setText("234 * 10^9"); + amount.setForeground(new Color(143, 170, 220)); + amount.setBackground(Color.WHITE); + this.add(amount); + + URL payoutIconURL = ClassLoader.getSystemResource("buttons/wallet-pay.png"); + ImageIcon payOutIcon = new ImageIcon(payoutIconURL); + JLabel payOutLabel = new JLabel(payOutIcon); + payOutLabel.setToolTipText("Payout for all address list"); + payOutLabel.setCursor(new Cursor(Cursor.HAND_CURSOR)); + payOutLabel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + System.out.println("boom"); + } + }); + + this.add(payOutLabel); + } +} diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/WalletWindow.java b/ethereumj-core/src/main/java/org/ethereum/gui/WalletWindow.java new file mode 100644 index 00000000..cff8526a --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/gui/WalletWindow.java @@ -0,0 +1,63 @@ +package org.ethereum.gui; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.net.URL; + +/** + * www.ethereumJ.com + * User: Roman Mandeleil + * Created on: 17/05/14 12:00 + */ +public class WalletWindow extends JFrame { + + + public WalletWindow() { + + java.net.URL url = ClassLoader.getSystemResource("ethereum-icon.png"); + Toolkit kit = Toolkit.getDefaultToolkit(); + Image img = kit.createImage(url); + this.setIconImage(img); + setTitle("Ethereum Wallet"); + setSize(490, 370); + setLocation(215, 280); + setBackground(Color.WHITE); + setResizable(false); + + Container contentPane = this.getContentPane(); + contentPane.setLayout(new FlowLayout()); + contentPane.setBackground(Color.WHITE); + + WalletAddressPanel panel1 = new WalletAddressPanel(); + WalletAddressPanel panel2 = new WalletAddressPanel(); + WalletAddressPanel panel3 = new WalletAddressPanel(); + WalletAddressPanel panel4 = new WalletAddressPanel(); + WalletSumPanel panel5 = new WalletSumPanel(); + + contentPane.add(panel1); + contentPane.add(panel2); + contentPane.add(panel3); + contentPane.add(panel4); + contentPane.add(panel5); + + URL addAddressIconURL = ClassLoader.getSystemResource("buttons/add-address.png"); + ImageIcon addAddressIcon = new ImageIcon(addAddressIconURL); + JLabel addAddressLabel = new JLabel(addAddressIcon); + addAddressLabel.setToolTipText("Add new address"); + addAddressLabel.setCursor(new Cursor(Cursor.HAND_CURSOR)); + addAddressLabel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + System.out.println("boom"); + } + }); + + contentPane.add(addAddressLabel); + + } +} diff --git a/ethereumj-core/src/main/java/org/ethereum/net/message/BlocksMessage.java b/ethereumj-core/src/main/java/org/ethereum/net/message/BlocksMessage.java index 2453a22c..9086f774 100644 --- a/ethereumj-core/src/main/java/org/ethereum/net/message/BlocksMessage.java +++ b/ethereumj-core/src/main/java/org/ethereum/net/message/BlocksMessage.java @@ -55,12 +55,8 @@ public class BlocksMessage extends Message { StringBuffer sb = new StringBuffer(); for (Block blockData : this.getBlockDataList()) { - sb.append(" ").append(blockData.toString()).append("\n"); + sb.append(" ").append(blockData.toFlatString()).append("\n"); - List transactions = blockData.getTransactionsList(); - for (Transaction transactionData : transactions) { - sb.append("[").append(transactionData).append("]\n"); - } } return "Blocks Message [\n" + sb.toString() + " ]"; } diff --git a/ethereumj-core/src/main/resources/buttons/add-address.png b/ethereumj-core/src/main/resources/buttons/add-address.png new file mode 100644 index 0000000000000000000000000000000000000000..ba9206b85cff64e86b71cfe0f18d61717ce31c02 GIT binary patch literal 3287 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 V6j>(YkW>Hw002ovPDHLkV1m^jRR#b6 literal 0 HcmV?d00001 diff --git a/ethereumj-core/src/main/resources/buttons/wallet-pay.png b/ethereumj-core/src/main/resources/buttons/wallet-pay.png new file mode 100644 index 0000000000000000000000000000000000000000..7b1958171d0bc058a619e32c58fa210cb8e7574d GIT binary patch literal 3848 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 KMNUMnLSTZ`#z1KR literal 0 HcmV?d00001