diff --git a/ethereumj-core/src/main/java/org/ethereum/geodb/IpGeoDB.java b/ethereumj-core/src/main/java/org/ethereum/geodb/IpGeoDB.java
new file mode 100644
index 00000000..b691c989
--- /dev/null
+++ b/ethereumj-core/src/main/java/org/ethereum/geodb/IpGeoDB.java
@@ -0,0 +1,49 @@
+package org.ethereum.geodb;
+
+import com.maxmind.geoip.Location;
+import com.maxmind.geoip.LookupService;
+import com.maxmind.geoip2.DatabaseReader;
+import com.maxmind.geoip2.exception.GeoIp2Exception;
+import com.maxmind.geoip2.model.CityResponse;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+/**
+ * www.ethereumJ.com
+ * User: Roman Mandeleil
+ * Created on: 24/04/14 20:11
+ */
+public class IpGeoDB {
+
+ static{
+ try {
+
+ URL geiIpDBFile = ClassLoader.getSystemResource("GeoLiteCity.dat");
+ File file = new File(geiIpDBFile.toURI());
+ cl = new LookupService(file);
+
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static LookupService cl;
+
+
+ public static Location getLocationForIp(InetAddress ip){
+ try {
+ return cl.getLocation(ip);
+ } catch (Throwable e) {
+
+ // todo: think about this exception, maybe you can do something more reasonable
+ System.out.println(e.getMessage());
+// e.printStackTrace();
+ }
+
+ return null;
+ }
+}
diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/ConnectionConsole.java b/ethereumj-core/src/main/java/org/ethereum/gui/ConnectionConsole.java
new file mode 100644
index 00000000..f63853d8
--- /dev/null
+++ b/ethereumj-core/src/main/java/org/ethereum/gui/ConnectionConsole.java
@@ -0,0 +1,103 @@
+package org.ethereum.gui;
+
+import org.ethereum.net.client.ClientPeer;
+import org.fife.ui.rsyntaxtextarea.*;
+import org.fife.ui.rtextarea.RTextScrollPane;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.io.IOException;
+import java.util.TimerTask;
+
+/**
+ * A simple example showing how to modify the fonts and colors used in an
+ * RSyntaxTextArea. There are two methods to do this - via the Java API, and via
+ * an XML file. The latter method is preferred since it's more modular, and
+ * provides a way for your users to customize RSTA in your application.
+ *
+ * This example uses RSyntaxTextArea 2.0.1.
+ *
+ * Project Home: http://fifesoft.com/rsyntaxtextarea
+ * Downloads: https://sourceforge.net/projects/rsyntaxtextarea
+ */
+public class ConnectionConsole extends JFrame implements PeerListener{
+
+ private static final long serialVersionUID = 1L;
+
+ private RSyntaxTextArea textArea;
+
+
+ public ConnectionConsole() {
+
+ final ConnectionConsole thisConsole = this;
+
+ java.net.URL url = ClassLoader.getSystemResource("ethereum-icon.png");
+ Toolkit kit = Toolkit.getDefaultToolkit();
+ Image img = kit.createImage(url);
+ this.setIconImage(img);
+
+ JPanel cp = new JPanel(new BorderLayout());
+
+ textArea = new RSyntaxTextArea(16, 47);
+ textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_LISP);
+ textArea.setCodeFoldingEnabled(true);
+ textArea.setAntiAliasingEnabled(true);
+ RTextScrollPane sp = new RTextScrollPane(textArea);
+ cp.add(sp);
+
+ setContentPane(cp);
+ setTitle("Connection Console");
+// setDefaultCloseOperation(EXIT_ON_CLOSE);
+ pack();
+ setLocation(775, 390);
+
+ this.addComponentListener(new ComponentAdapter() {
+
+ @Override
+ public void componentShown(ComponentEvent e) {
+
+ Thread t = new Thread() {
+ public void run() {
+// new ClientPeer(thisConsole).connect("54.201.28.117", 30303);
+ new ClientPeer(thisConsole).connect("82.217.72.169", 30303);
+ }
+ };
+ t.start();
+
+
+
+ }
+
+ });
+
+ }
+
+ @Override
+ public void console(final String output) {
+
+
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ textArea.append(output);
+ textArea.append("\n");
+ textArea.setCaretPosition(textArea.getText().length());
+ }
+ });
+
+
+ }
+
+ public static void main(String[] args) {
+ // Start all Swing applications on the EDT.
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ new ConnectionConsole().setVisible(true);
+ }
+ });
+
+ }
+
+}
diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/PeerListener.java b/ethereumj-core/src/main/java/org/ethereum/gui/PeerListener.java
new file mode 100644
index 00000000..cf2c80fd
--- /dev/null
+++ b/ethereumj-core/src/main/java/org/ethereum/gui/PeerListener.java
@@ -0,0 +1,11 @@
+package org.ethereum.gui;
+
+/**
+ * www.ethereumJ.com
+ * User: Roman Mandeleil
+ * Created on: 30/04/14 11:32
+ */
+public interface PeerListener {
+
+ public void console(String output);
+}
diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/PeersTableModel.java b/ethereumj-core/src/main/java/org/ethereum/gui/PeersTableModel.java
new file mode 100644
index 00000000..8f27038f
--- /dev/null
+++ b/ethereumj-core/src/main/java/org/ethereum/gui/PeersTableModel.java
@@ -0,0 +1,166 @@
+package org.ethereum.gui;
+
+import com.maxmind.geoip.Location;
+import com.maxmind.geoip2.model.CityResponse;
+import org.ethereum.geodb.IpGeoDB;
+import org.ethereum.util.Utils;
+
+import javax.swing.*;
+import javax.swing.table.AbstractTableModel;
+import java.net.InetAddress;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * www.ethereumJ.com
+ * User: Roman Mandeleil
+ * Created on: 25/04/14 07:04
+ */
+public class PeersTableModel extends AbstractTableModel {
+
+ List peerInfoList = new ArrayList();
+
+ public PeersTableModel() {
+
+ generateRandomData();
+ }
+
+
+
+ public String getColumnName(int column) {
+
+ if (column == 0) return "Location";
+ if (column == 1) return "IP";
+ if (column == 2) return "Live";
+ else return "";
+ }
+
+ public boolean isCellEditable(int row, int column) {
+ return false;
+ }
+
+ public Class getColumnClass(int column) {
+ if (column == 0) return ImageIcon.class;
+ if (column == 1) return String.class;
+ if (column == 2) return ImageIcon.class;
+ else return String.class;
+ }
+
+ public Object getValueAt(int row, int column) {
+
+ PeerInfo peerInfo = peerInfoList.get(row);
+
+ if (column == 0){
+
+ String countryCode = peerInfo.getLocation().countryCode;
+ URL flagURL = ClassLoader.getSystemResource("flags/" + countryCode + ".png");
+ ImageIcon flagIcon = new ImageIcon(flagURL);
+
+
+ return flagIcon;
+ }
+
+ if (column == 1) return peerInfo.getIp().getHostAddress();
+ if (column == 2) {
+
+ Random random = new Random();
+ boolean isConnected = random.nextBoolean();
+
+ ImageIcon flagIcon = null;
+ if (peerInfo.connected){
+
+ flagIcon = Utils.getImageIcon("connected.png");
+ } else {
+
+ flagIcon = Utils.getImageIcon("disconnected.png");
+ }
+
+
+ return flagIcon;
+ }
+
+ else return "";
+ }
+
+ public int getRowCount() {
+ return this.peerInfoList.size();
+ }
+
+ public int getColumnCount() {
+ return 3;
+ }
+
+
+
+ // todo: delete it when stabilized
+ private void generateRandomData(){
+
+ List ips = new ArrayList();
+ ips.add("206.223.168.190");
+ ips.add("94.210.200.192");
+ ips.add("88.69.198.198");
+ ips.add("62.78.198.208");
+ ips.add("71.202.162.40");
+ ips.add("78.55.236.218");
+ ips.add("94.197.120.80");
+ ips.add("85.65.126.45");
+
+ ips.add("110.77.217.185");
+ ips.add("64.231.9.30");
+ ips.add("162.243.203.121");
+ ips.add("82.217.72.169");
+
+ ips.add("99.231.80.166");
+ ips.add("131.104.252.4");
+ ips.add("54.204.10.41");
+ ips.add("54.201.28.117");
+ ips.add("82.240.16.5");
+ ips.add("74.79.23.119");
+
+
+ for (String peer : ips){
+
+ try {
+
+ InetAddress addr = InetAddress.getByName(peer);
+ Location cr = IpGeoDB.getLocationForIp(addr);
+
+ peerInfoList.add(new PeerInfo(cr, addr));
+
+ } catch (UnknownHostException e) {e.printStackTrace(); }
+ }
+ }
+
+ private class PeerInfo{
+
+ Location location;
+ InetAddress ip;
+ boolean connected;
+
+ private PeerInfo(Location location, InetAddress ip) {
+ this.location = location;
+ this.ip = ip;
+
+ Random random = new Random();
+ connected = random.nextBoolean();
+ }
+
+ private InetAddress getIp() {
+ return ip;
+ }
+
+
+ private Location getLocation() {
+ return location;
+ }
+
+ private boolean isConnected() {
+ return connected;
+ }
+ }
+}
+
+
diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/SerpentEditor.java b/ethereumj-core/src/main/java/org/ethereum/gui/SerpentEditor.java
new file mode 100644
index 00000000..40be180c
--- /dev/null
+++ b/ethereumj-core/src/main/java/org/ethereum/gui/SerpentEditor.java
@@ -0,0 +1,121 @@
+package org.ethereum.gui;
+
+import org.ethereum.serpent.SerpentCompiler;
+import org.fife.ui.rsyntaxtextarea.AbstractTokenMakerFactory;
+import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
+import org.fife.ui.rsyntaxtextarea.TokenMakerFactory;
+import org.fife.ui.rtextarea.RTextScrollPane;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+
+/**
+ * www.ethereumJ.com
+ * User: Roman Mandeleil
+ * Created on: 24/04/14 11:32
+ */
+
+
+public class SerpentEditor extends JFrame {
+
+ private String codeSample = "\n\n\n" +
+ "" +
+ "if !contract.storage[msg.data[0]]:\n" +
+ " contract.storage[msg.data[0]] = msg.data[1]\n" +
+ " return(1)\n" +
+ "else:\n" +
+ " return(0)\n";
+
+
+ private static final long serialVersionUID = 1L;
+
+ public SerpentEditor() {
+
+ final JPanel cp = new JPanel(new BorderLayout());
+ final JFrame mainWindow = this;
+
+ java.net.URL url = ClassLoader.getSystemResource("ethereum-icon.png");
+ Toolkit kit = Toolkit.getDefaultToolkit();
+ Image img = kit.createImage(url);
+ this.setIconImage(img);
+ this.setLocation(30, 80);
+
+ AbstractTokenMakerFactory atmf = (AbstractTokenMakerFactory)TokenMakerFactory.getDefaultInstance();
+ atmf.putMapping("text/serpent", "org.ethereum.gui.SerpentTokenMaker");
+
+ final RSyntaxTextArea codeArea = new RSyntaxTextArea(32, 80);
+ codeArea.setSyntaxEditingStyle("text/serpent");
+ codeArea.setCodeFoldingEnabled(true);
+ codeArea.setAntiAliasingEnabled(true);
+ codeArea.setText(codeSample);
+
+ RTextScrollPane sp = new RTextScrollPane(codeArea);
+
+ sp.setFoldIndicatorEnabled(true);
+ cp.setLayout(new BorderLayout());
+
+ final JSplitPane splitPanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
+ splitPanel.setOneTouchExpandable(true);
+ splitPanel.setDividerSize(5);
+ splitPanel.setContinuousLayout(true);
+
+ cp.add(splitPanel, BorderLayout.CENTER);
+ splitPanel.add(sp);
+
+ final JTextArea result = new JTextArea();
+ result.setLineWrap(true);
+ result.setWrapStyleWord(true);
+ result.setVisible(false);
+
+ splitPanel.add(result);
+
+ JPanel controlsPanel = new JPanel();
+ FlowLayout fl = new FlowLayout(FlowLayout.LEADING, 30, 5);
+ fl.setAlignment(FlowLayout.RIGHT);
+ controlsPanel.setLayout(fl);
+
+ JButton buildButton = new JButton("Build");
+ buildButton.addActionListener(new ActionListener() {
+
+ public void actionPerformed(ActionEvent e) {
+
+ String asmResult = "";
+ try {
+ asmResult = SerpentCompiler.compile(codeArea.getText());
+ } catch (Throwable th) {th.printStackTrace();}
+
+ splitPanel.setDividerLocation(0.7);
+
+ result.setVisible(true);
+ result.setText(asmResult);
+ }
+ });
+
+
+ controlsPanel.add(buildButton);
+
+ cp.add(controlsPanel, BorderLayout.SOUTH);
+
+
+ setContentPane(cp);
+ setTitle("Serpent Editor");
+// setDefaultCloseOperation(EXIT_ON_CLOSE);
+ pack();
+// setLocationRelativeTo(null);
+
+ }
+
+ public static void main(String[] args) {
+ // Start all Swing applications on the EDT.
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ new SerpentEditor().setVisible(true);
+ }
+ });
+ }
+
+
+}
\ No newline at end of file
diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/SerpentTokenMaker.java b/ethereumj-core/src/main/java/org/ethereum/gui/SerpentTokenMaker.java
new file mode 100644
index 00000000..f77c8f0b
--- /dev/null
+++ b/ethereumj-core/src/main/java/org/ethereum/gui/SerpentTokenMaker.java
@@ -0,0 +1,508 @@
+package org.ethereum.gui;
+
+import javax.swing.text.Segment;
+
+import org.fife.ui.rsyntaxtextarea.*;
+
+
+
+/**
+ * www.ethereumJ.com
+ * User: Roman Mandeleil
+ * Created on: 24/04/14 11:52
+ */
+
+public class SerpentTokenMaker extends AbstractTokenMaker {
+
+// http://fifesoft.com/rsyntaxtextarea/doc/CustomSyntaxHighlighting.html
+
+ protected final String operators = ".@:*<>=?|!";
+
+ private int currentTokenStart;
+ private int currentTokenType;
+
+ private boolean bracketVariable; // Whether a variable is of the format %{...}
+
+
+ /**
+ * Constructor.
+ */
+ public SerpentTokenMaker() {
+ super(); // Initializes tokensToHighlight.
+ }
+
+
+ /**
+ * Checks the token to give it the exact ID it deserves before
+ * being passed up to the super method.
+ *
+ * @param segment Segment
to get text from.
+ * @param start Start offset in segment
of token.
+ * @param end End offset in segment
of token.
+ * @param tokenType The token's type.
+ * @param startOffset The offset in the document at which the token occurs.
+ */
+ @Override
+ public void addToken(Segment segment, int start, int end, int tokenType, int startOffset) {
+
+ switch (tokenType) {
+ // Since reserved words, functions, and data types are all passed
+ // into here as "identifiers," we have to see what the token
+ // really is...
+ case Token.IDENTIFIER:
+ int value = wordsToHighlight.get(segment, start,end);
+ if (value!=-1)
+ tokenType = value;
+ break;
+ }
+
+ super.addToken(segment, start, end, tokenType, startOffset);
+
+ }
+
+
+ /**
+ * Returns the text to place at the beginning and end of a
+ * line to "comment" it in a this programming language.
+ *
+ * @return The start and end strings to add to a line to "comment"
+ * it out.
+ */
+ @Override
+ public String[] getLineCommentStartAndEnd() {
+ return new String[] { "#", null };
+ }
+
+
+ /**
+ * Returns whether tokens of the specified type should have "mark
+ * occurrences" enabled for the current programming language.
+ *
+ * @param type The token type.
+ * @return Whether tokens of this type should have "mark occurrences"
+ * enabled.
+ */
+ @Override
+ public boolean getMarkOccurrencesOfTokenType(int type) {
+ return type==Token.IDENTIFIER || type==Token.VARIABLE;
+ }
+
+
+ /**
+ * Returns the words to highlight for Windows batch files.
+ *
+ * @return A TokenMap
containing the words to highlight for
+ * Windows batch files.
+ * @see org.fife.ui.rsyntaxtextarea.AbstractTokenMaker#getWordsToHighlight
+ */
+ @Override
+ public TokenMap getWordsToHighlight() {
+
+ TokenMap tokenMap = new TokenMap(true); // Ignore case.
+
+ int reservedWord = Token.RESERVED_WORD;
+ tokenMap.put("set", reservedWord);
+ tokenMap.put("if", reservedWord);
+ tokenMap.put("else", reservedWord);
+ tokenMap.put("elif", reservedWord);
+ tokenMap.put("seq", reservedWord);
+ tokenMap.put("while", reservedWord);
+ tokenMap.put("byte", reservedWord);
+ tokenMap.put("access", reservedWord);
+ tokenMap.put("arrset", reservedWord);
+ tokenMap.put("set_and_inc", reservedWord);
+ tokenMap.put("array", reservedWord);
+ tokenMap.put("getch", reservedWord);
+ tokenMap.put("setch", reservedWord);
+ tokenMap.put("string", reservedWord);
+ tokenMap.put("send", reservedWord);
+ tokenMap.put("create", reservedWord);
+ tokenMap.put("sha3", reservedWord);
+ tokenMap.put("sha3bytes", reservedWord);
+ tokenMap.put("sload", reservedWord);
+ tokenMap.put("sstore", reservedWord);
+ tokenMap.put("calldataload", reservedWord);
+ tokenMap.put("id", reservedWord);
+ tokenMap.put("return", reservedWord);
+ tokenMap.put("suicide", reservedWord);
+
+
+ tokenMap.put("stop", reservedWord);
+
+ int function = Token.FUNCTION;
+ tokenMap.put("msg", function);
+ tokenMap.put("contract", function);
+ tokenMap.put("block", function);
+ tokenMap.put("tx", function);
+
+
+ return tokenMap;
+
+ }
+
+
+ /**
+ * Returns a peerInfoList of tokens representing the given text.
+ *
+ * @param text The text to break into tokens.
+ * @param startTokenType The token with which to start tokenizing.
+ * @param startOffset The offset at which the line of tokens begins.
+ * @return A linked peerInfoList of tokens representing text
.
+ */
+ public Token getTokenList(Segment text, int startTokenType, final int startOffset) {
+
+ resetTokenList();
+
+ char[] array = text.array;
+ int offset = text.offset;
+ int count = text.count;
+ int end = offset + count;
+
+ // See, when we find a token, its starting position is always of the form:
+ // 'startOffset + (currentTokenStart-offset)'; but since startOffset and
+ // offset are constant, tokens' starting positions become:
+ // 'newStartOffset+currentTokenStart' for one less subtraction operation.
+ int newStartOffset = startOffset - offset;
+
+ currentTokenStart = offset;
+ currentTokenType = startTokenType;
+
+//beginning:
+ for (int i=offset; i-1) {
+ addToken(text, currentTokenStart,i, Token.OPERATOR, newStartOffset+currentTokenStart);
+ currentTokenType = Token.NULL;
+ break;
+ }
+ else {
+ currentTokenType = Token.IDENTIFIER;
+ break;
+ }
+
+ } // End of switch (c).
+
+ break;
+
+ case Token.WHITESPACE:
+
+ switch (c) {
+
+ case ' ':
+ case '\t':
+ break; // Still whitespace.
+
+ case '"':
+ addToken(text, currentTokenStart,i-1, Token.WHITESPACE, newStartOffset+currentTokenStart);
+ currentTokenStart = i;
+ currentTokenType = Token.ERROR_STRING_DOUBLE;
+ break;
+
+ case '%':
+ addToken(text, currentTokenStart,i-1, Token.WHITESPACE, newStartOffset+currentTokenStart);
+ currentTokenStart = i;
+ currentTokenType = Token.VARIABLE;
+ break;
+
+ // The "separators".
+ case '(':
+ case ')':
+ addToken(text, currentTokenStart,i-1, Token.WHITESPACE, newStartOffset+currentTokenStart);
+ addToken(text, i,i, Token.SEPARATOR, newStartOffset+i);
+ currentTokenType = Token.NULL;
+ break;
+
+ // The "separators2".
+ case ',':
+ case ';':
+ addToken(text, currentTokenStart,i-1, Token.WHITESPACE, newStartOffset+currentTokenStart);
+ addToken(text, i,i, Token.IDENTIFIER, newStartOffset+i);
+ currentTokenType = Token.NULL;
+ break;
+
+ // Newer version of EOL comments, or a label
+ case ':':
+ addToken(text, currentTokenStart,i-1, Token.WHITESPACE, newStartOffset+currentTokenStart);
+ currentTokenStart = i;
+ // If the previous (whitespace) token was the first token
+ // added, this is a new-style comment or a label
+ if (firstToken.getNextToken()==null) {
+ if (i-1) {
+ addToken(text, currentTokenStart,i, Token.OPERATOR, newStartOffset+currentTokenStart);
+ currentTokenType = Token.NULL;
+ break;
+ }
+ else {
+ currentTokenType = Token.IDENTIFIER;
+ }
+
+ } // End of switch (c).
+
+ break;
+
+ default: // Should never happen
+ case Token.IDENTIFIER:
+
+ switch (c) {
+
+ case ' ':
+ case '\t':
+ // Check for REM comments.
+ if (i-currentTokenStart==3 &&
+ (array[i-3]=='r' || array[i-3]=='R') &&
+ (array[i-2]=='e' || array[i-2]=='E') &&
+ (array[i-1]=='m' || array[i-1]=='M')) {
+ currentTokenType = Token.COMMENT_EOL;
+ break;
+ }
+ addToken(text, currentTokenStart,i-1, Token.IDENTIFIER, newStartOffset+currentTokenStart);
+ currentTokenStart = i;
+ currentTokenType = Token.WHITESPACE;
+ break;
+
+ case '"':
+ addToken(text, currentTokenStart,i-1, Token.IDENTIFIER, newStartOffset+currentTokenStart);
+ currentTokenStart = i;
+ currentTokenType = Token.ERROR_STRING_DOUBLE;
+ break;
+
+ case '%':
+ addToken(text, currentTokenStart,i-1, Token.IDENTIFIER, newStartOffset+currentTokenStart);
+ currentTokenStart = i;
+ currentTokenType = Token.VARIABLE;
+ break;
+
+ // Should be part of identifiers, but not at end of "REM".
+ case '\\':
+ // Check for REM comments.
+ if (i-currentTokenStart==3 &&
+ (array[i-3]=='r' || array[i-3]=='R') &&
+ (array[i-2]=='e' || array[i-2]=='E') &&
+ (array[i-1]=='m' || array[i-1]=='M')) {
+ currentTokenType = Token.COMMENT_EOL;
+ }
+ break;
+
+// case '.':
+ case '_':
+ break; // Characters good for identifiers.
+
+ // The "separators".
+ case '(':
+ case ')':
+ addToken(text, currentTokenStart,i-1, Token.IDENTIFIER, newStartOffset+currentTokenStart);
+ addToken(text, i,i, Token.SEPARATOR, newStartOffset+i);
+ currentTokenType = Token.NULL;
+ break;
+
+ // The "separators2".
+ case ',':
+ case ';':
+ addToken(text, currentTokenStart,i-1, Token.IDENTIFIER, newStartOffset+currentTokenStart);
+ addToken(text, i,i, Token.IDENTIFIER, newStartOffset+i);
+ currentTokenType = Token.NULL;
+ break;
+
+ default:
+
+ // Just to speed things up a tad, as this will usually be the case.
+ if (RSyntaxUtilities.isLetterOrDigit(c) || c=='\\') {
+ break;
+ }
+
+ int indexOf = operators.indexOf(c);
+ if (indexOf>-1) {
+ addToken(text, currentTokenStart,i-1, Token.IDENTIFIER, newStartOffset+currentTokenStart);
+ addToken(text, i,i, Token.OPERATOR, newStartOffset+i);
+ currentTokenType = Token.NULL;
+ break;
+ }
+
+ // Otherwise, fall through and assume we're still okay as an IDENTIFIER...
+
+ } // End of switch (c).
+
+ break;
+
+ case Token.COMMENT_EOL:
+ i = end - 1;
+ addToken(text, currentTokenStart,i, Token.COMMENT_EOL, newStartOffset+currentTokenStart);
+ // We need to set token type to null so at the bottom we don't add one more token.
+ currentTokenType = Token.NULL;
+ break;
+
+ case Token.PREPROCESSOR: // Used for labels
+ i = end - 1;
+ addToken(text, currentTokenStart,i, Token.PREPROCESSOR, newStartOffset+currentTokenStart);
+ // We need to set token type to null so at the bottom we don't add one more token.
+ currentTokenType = Token.NULL;
+ break;
+
+ case Token.ERROR_STRING_DOUBLE:
+
+ if (c=='"') {
+ addToken(text, currentTokenStart,i, Token.LITERAL_STRING_DOUBLE_QUOTE, newStartOffset+currentTokenStart);
+ currentTokenStart = i + 1;
+ currentTokenType = Token.NULL;
+ }
+ // Otherwise, we're still an unclosed string...
+
+ break;
+
+ case Token.VARIABLE:
+
+ if (i==currentTokenStart+1) { // first character after '%'.
+ bracketVariable = false;
+ switch (c) {
+ case '{':
+ bracketVariable = true;
+ break;
+ default:
+ if (RSyntaxUtilities.isLetter(c) || c==' ') { // No tab, just space; spaces are okay in variable names.
+ break;
+ }
+ else if (RSyntaxUtilities.isDigit(c)) { // Single-digit command-line argument ("%1").
+ addToken(text, currentTokenStart,i, Token.VARIABLE, newStartOffset+currentTokenStart);
+ currentTokenType = Token.NULL;
+ break;
+ }
+ else { // Anything else, ???.
+ addToken(text, currentTokenStart,i-1, Token.VARIABLE, newStartOffset+currentTokenStart); // ???
+ i--;
+ currentTokenType = Token.NULL;
+ break;
+ }
+ } // End of switch (c).
+ }
+ else { // Character other than first after the '%'.
+ if (bracketVariable==true) {
+ if (c=='}') {
+ addToken(text, currentTokenStart,i, Token.VARIABLE, newStartOffset+currentTokenStart);
+ currentTokenType = Token.NULL;
+ }
+ }
+ else {
+ if (c=='%') {
+ addToken(text, currentTokenStart,i, Token.VARIABLE, newStartOffset+currentTokenStart);
+ currentTokenType = Token.NULL;
+ }
+ }
+ break;
+ }
+ break;
+
+ } // End of switch (currentTokenType).
+
+ } // End of for (int i=offset; i
+ *
+ * This example uses RSyntaxTextArea 2.0.1.
+ *
+ * Project Home: http://fifesoft.com/rsyntaxtextarea
+ * Downloads: https://sourceforge.net/projects/rsyntaxtextarea
+ */
+public class SyntaxSchemeDemo extends JFrame implements ActionListener {
+
+ private static final long serialVersionUID = 1L;
+
+ private RSyntaxTextArea textArea;
+
+ private static final String text = "public class ExampleSource {\n\n"
+ + " // Check out the crazy modified styles!\n"
+ + " public static void main(String[] args) {\n"
+ + " System.out.println(\"Hello, world!\");\n" + " }\n\n"
+ + "}\n";
+
+ public SyntaxSchemeDemo() {
+
+ java.net.URL url = ClassLoader.getSystemResource("ethereum-icon.png");
+ Toolkit kit = Toolkit.getDefaultToolkit();
+ Image img = kit.createImage(url);
+ this.setIconImage(img);
+
+ JPanel cp = new JPanel(new BorderLayout());
+
+ textArea = new RSyntaxTextArea(20, 60);
+ textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
+ textArea.setCodeFoldingEnabled(true);
+ textArea.setAntiAliasingEnabled(true);
+ RTextScrollPane sp = new RTextScrollPane(textArea);
+ cp.add(sp);
+
+// JPanel buttons = new JPanel();
+// buttons.setLayout(new FlowLayout());
+// buttons.add(new JButton("build"));
+//
+// cp.add(buttons, BorderLayout.SOUTH);
+
+ textArea.setText(text);
+
+ JMenuBar mb = new JMenuBar();
+ JMenu menu = new JMenu("File");
+ mb.add(menu);
+ JMenuItem changeStyleProgrammaticallyItem = new JMenuItem(
+ "Change Style Programmatically");
+ changeStyleProgrammaticallyItem
+ .setActionCommand("ChangeProgrammatically");
+ changeStyleProgrammaticallyItem.addActionListener(this);
+ menu.add(changeStyleProgrammaticallyItem);
+ JMenuItem changeStyleViaThemesItem = new JMenuItem(
+ "Change Style via Theme XML");
+ changeStyleViaThemesItem.setActionCommand("ChangeViaThemes");
+ changeStyleViaThemesItem.addActionListener(this);
+ menu.add(changeStyleViaThemesItem);
+// setJMenuBar(mb);
+
+ setContentPane(cp);
+ setTitle("Connection Console");
+ setDefaultCloseOperation(EXIT_ON_CLOSE);
+ pack();
+ setLocationRelativeTo(null);
+
+ }
+
+ /**
+ * Listens for the selection of a menu item and performs an action
+ * accordingly.
+ */
+ public void actionPerformed(ActionEvent e) {
+ String command = e.getActionCommand();
+ if ("ChangeProgrammatically".equals(command)) {
+ changeStyleProgrammatically();
+ } else if ("ChangeViaThemes".equals(command)) {
+ changeStyleViaThemeXml();
+ }
+ }
+
+ /**
+ * Changes the styles used in the editor programmatically.
+ */
+ private void changeStyleProgrammatically() {
+
+ // Set the font for all token types.
+ setFont(textArea, new Font("Comic Sans MS", Font.PLAIN, 16));
+
+ // Change a few things here and there.
+ SyntaxScheme scheme = textArea.getSyntaxScheme();
+ scheme.getStyle(Token.RESERVED_WORD).background = Color.pink;
+ scheme.getStyle(Token.DATA_TYPE).foreground = Color.blue;
+ scheme.getStyle(Token.LITERAL_STRING_DOUBLE_QUOTE).underline = true;
+ scheme.getStyle(Token.COMMENT_EOL).font = new Font("Georgia",
+ Font.ITALIC, 12);
+
+ textArea.revalidate();
+
+ }
+
+ /**
+ * Changes the styles used by the editor via an XML file specification. This
+ * method is preferred because of its ease and modularity.
+ */
+ private void changeStyleViaThemeXml() {
+ try {
+ Theme theme = Theme.load(getClass().getResourceAsStream(
+ "/eclipse_theme.xml"));
+ theme.apply(textArea);
+ } catch (IOException ioe) { // Never happens
+ ioe.printStackTrace();
+ }
+ }
+
+ /**
+ * Set the font for all token types.
+ *
+ * @param textArea The text area to modify.
+ * @param font The font to use.
+ */
+ public static void setFont(RSyntaxTextArea textArea, Font font) {
+ if (font != null) {
+ SyntaxScheme ss = textArea.getSyntaxScheme();
+ ss = (SyntaxScheme) ss.clone();
+ for (int i = 0; i < ss.getStyleCount(); i++) {
+ if (ss.getStyle(i) != null) {
+ ss.getStyle(i).font = font;
+ }
+ }
+ textArea.setSyntaxScheme(ss);
+ textArea.setFont(font);
+ }
+ }
+
+ public static void main(String[] args) {
+ // Start all Swing applications on the EDT.
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ new SyntaxSchemeDemo().setVisible(true);
+ }
+ });
+ }
+
+}
diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/ToolBar.java b/ethereumj-core/src/main/java/org/ethereum/gui/ToolBar.java
new file mode 100644
index 00000000..becd2806
--- /dev/null
+++ b/ethereumj-core/src/main/java/org/ethereum/gui/ToolBar.java
@@ -0,0 +1,126 @@
+package org.ethereum.gui;
+
+import samples.PeersTableMain;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+/**
+ * www.ethereumJ.com
+ * User: Roman Mandeleil
+ * Created on: 30/04/14 06:29
+ */
+public class ToolBar extends JFrame {
+
+ public ToolBar() throws HeadlessException {
+ final JPanel cp = new JPanel(new FlowLayout());
+ cp.setBackground(Color.WHITE);
+
+ java.net.URL url = ClassLoader.getSystemResource("ethereum-icon.png");
+ Toolkit kit = Toolkit.getDefaultToolkit();
+ Image img = kit.createImage(url);
+ this.setIconImage(img);
+ this.setSize(350, 130);
+ this.setLocation(460, 25);
+ this.setAlwaysOnTop(true);
+ this.setResizable(false);
+ this.setBackground(Color.WHITE);
+
+ setTitle("EthereumJ Studio");
+ setDefaultCloseOperation(EXIT_ON_CLOSE);
+
+ this.setContentPane(cp);
+
+
+ java.net.URL imageURL_1 = ClassLoader.getSystemResource("buttons/feedly.png");
+ ImageIcon image_1 = new ImageIcon(imageURL_1);
+
+ java.net.URL imageURL_2 = ClassLoader.getSystemResource("buttons/winamp.png");
+ ImageIcon image_2 = new ImageIcon(imageURL_2);
+
+ java.net.URL imageURL_3 = ClassLoader.getSystemResource("buttons/browser.png");
+ ImageIcon image_3 = new ImageIcon(imageURL_3);
+
+
+
+ JToggleButton editorToggle = new JToggleButton("");
+ editorToggle.setIcon(image_1);
+ editorToggle.setContentAreaFilled(true);
+ editorToggle.setToolTipText("Serpent Editor");
+ editorToggle.setBackground(Color.WHITE);
+ editorToggle.setBorderPainted(false);
+ editorToggle.setFocusPainted(false);
+ editorToggle.setCursor(new Cursor(Cursor.HAND_CURSOR));
+ editorToggle.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ new SerpentEditor().setVisible(true);
+ }
+ });
+ }
+ });
+
+ JToggleButton logToggle = new JToggleButton();
+ logToggle.setIcon(image_2);
+ logToggle.setToolTipText("Log Console");
+ logToggle.setContentAreaFilled(true);
+ logToggle.setBackground(Color.WHITE);
+ logToggle.setBorderPainted(false);
+ logToggle.setFocusPainted(false);
+ logToggle.setCursor(new Cursor(Cursor.HAND_CURSOR));
+ logToggle.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ new ConnectionConsole().setVisible(true);
+ }
+ });
+ }
+ });
+
+ JToggleButton peersToggle = new JToggleButton();
+ peersToggle.setIcon(image_3);
+ peersToggle.setToolTipText("Peers");
+ peersToggle.setContentAreaFilled(true);
+ peersToggle.setBackground(Color.WHITE);
+ peersToggle.setBorderPainted(false);
+ peersToggle.setFocusPainted(false);
+ peersToggle.setCursor(new Cursor(Cursor.HAND_CURSOR));
+ peersToggle.addActionListener( new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ PeersTableMain mainFrame = new PeersTableMain();
+ mainFrame.setVisible( true );
+// mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+
+ }
+ });
+
+
+ cp.add(editorToggle);
+ cp.add(logToggle);
+ cp.add(peersToggle);
+
+
+
+
+
+ }
+
+ public static void main(String args[]){
+
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ new ToolBar().setVisible(true);
+ }
+ });
+
+ }
+
+}
diff --git a/ethereumj-core/src/main/java/org/ethereum/manager/MainData.java b/ethereumj-core/src/main/java/org/ethereum/manager/MainData.java
new file mode 100644
index 00000000..d66e8981
--- /dev/null
+++ b/ethereumj-core/src/main/java/org/ethereum/manager/MainData.java
@@ -0,0 +1,42 @@
+package org.ethereum.manager;
+
+import com.maxmind.geoip.Location;
+import com.maxmind.geoip2.model.CityResponse;
+import com.maxmind.geoip2.record.Country;
+import org.ethereum.geodb.IpGeoDB;
+import org.ethereum.net.vo.PeerData;
+
+import java.util.*;
+
+/**
+ * www.ethereumJ.com
+ * User: Roman Mandeleil
+ * Created on: 21/04/14 20:35
+ */
+public class MainData {
+
+ private Set peers = Collections.synchronizedSet(new HashSet());
+
+ private List blocks = Collections.synchronizedList(new ArrayList());
+ private List transactions = Collections.synchronizedList(new ArrayList());
+
+ public static MainData instance = new MainData();
+
+ public void addPeers(List newPeers){
+ this.peers.addAll(newPeers);
+
+
+ for (PeerData peerData : this.peers){
+
+ Location location = IpGeoDB.getLocationForIp(peerData.getInetAddress());
+ if (location != null)
+ System.out.println("Hello: " + " [" + peerData.getInetAddress().toString()
+ + "] " + location.countryName);
+
+ }
+ }
+
+ public void addBlocks(List blocks){}
+ public void addTransactions(List transactions){}
+
+}
diff --git a/ethereumj-core/src/main/java/org/ethereum/net/MessageDeserializer.java b/ethereumj-core/src/main/java/org/ethereum/net/MessageDeserializer.java
new file mode 100644
index 00000000..597f1ebb
--- /dev/null
+++ b/ethereumj-core/src/main/java/org/ethereum/net/MessageDeserializer.java
@@ -0,0 +1,121 @@
+package org.ethereum.net;
+
+/**
+ * www.openchain.info
+ * User: Roman Mandeleil
+ * Created on: 04/04/14 00:51
+ */
+public class MessageDeserializer {
+
+
+ /**
+ * Get exactly one message payload
+ */
+ public static void deserialize(byte [] msgData, int level, int startPos, int endPos){
+
+ if (msgData == null || msgData.length == 0) return ;
+ int pos = startPos;
+
+ while(pos < endPos){
+
+ // It's a list with a payload more than 55 bytes
+ // data[0] - 0xF7 = how many next bytes allocated
+ // for the length of the list
+ if ((msgData[pos] & 0xFF) >= 0xF7){
+
+ byte lenghtOfLenght = (byte) (msgData[pos] - 0xF7);
+ byte pow = (byte) (lenghtOfLenght - 1);
+
+ int length = 0;
+
+ for (int i = 1; i <= lenghtOfLenght; ++i){
+
+ length += msgData[pos + i] << (8 * pow);
+ pow--;
+ }
+
+ // now we can parse an item for data[1]..data[length]
+ System.out.println("-- level: [" + level + "] Found big list length: " + length);
+
+ deserialize(msgData, level + 1, pos + lenghtOfLenght + 1, pos + lenghtOfLenght + length);
+
+ pos += lenghtOfLenght + length + 1 ;
+ continue;
+ }
+
+
+ // It's a list with a payload less than 55 bytes
+ if ((msgData[pos] & 0xFF) >= 0xC0 && (msgData[pos] & 0xFF) < 0xF7){
+
+ byte length = (byte) (msgData[pos] - 0xC0);
+
+ System.out.println("-- level: [" + level + "] Found small list length: " + length);
+
+ deserialize(msgData, level + 1, pos + 1, pos + length + 1);
+
+ pos += 1 + length;
+ continue;
+ }
+
+
+ // It's an item with a payload more than 55 bytes
+ // data[0] - 0xB7 = how much next bytes allocated for
+ // the length of the string
+ if ((msgData[pos] & 0xFF) >= 0xB7 && (msgData[pos] & 0xFF) < 0xC0) {
+
+ byte lenghtOfLenght = (byte) (msgData[pos] - 0xB7);
+ byte pow = (byte) (lenghtOfLenght - 1);
+
+ int length = 0;
+
+ for (int i = 1; i <= lenghtOfLenght; ++i){
+
+ length += msgData[pos + i] << (8 * pow);
+ pow--;
+ }
+
+
+ // now we can parse an item for data[1]..data[length]
+ System.out.println("-- level: [" + level + "] Found big item length: " + length);
+ pos += lenghtOfLenght + length + 1 ;
+
+ continue;
+ }
+
+
+ // It's an item less than 55 bytes long,
+ // data[0] - 0x80 == lenght of the item
+ if ((msgData[pos] & 0xFF) > 0x80 && (msgData[pos] & 0xFF) < 0xB7) {
+
+ byte length = (byte) (msgData[pos] - 0x80);
+
+ System.out.println("-- level: [" + level + "] Found small item length: " + length);
+ pos += 1 + length;
+
+ continue;
+ }
+
+ // null item
+ if ((msgData[pos] & 0xFF) == 0x80){
+ System.out.println("-- level: [" + level + "] Found null item: ");
+ pos += 1;
+ continue;
+ }
+
+ // single byte item
+ if ((msgData[pos] & 0xFF) < 0x80) {
+ System.out.println("-- level: [" + level + "] Found single item: ");
+ pos += 1;
+ continue;
+ }
+
+
+
+ }
+
+
+
+
+ }
+
+}
diff --git a/ethereumj-core/src/main/java/org/ethereum/net/RLP.java b/ethereumj-core/src/main/java/org/ethereum/net/RLP.java
new file mode 100644
index 00000000..62c598c2
--- /dev/null
+++ b/ethereumj-core/src/main/java/org/ethereum/net/RLP.java
@@ -0,0 +1,1102 @@
+package org.ethereum.net;
+
+import org.bouncycastle.util.encoders.Hex;
+import org.ethereum.net.rlp.RLPItem;
+import org.ethereum.net.rlp.RLPList;
+import org.ethereum.util.Utils;
+
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.util.*;
+
+/**
+
+ */
+public class RLP {
+
+
+ public static byte decodeOneByteItem(byte[] data, int index){
+
+ // null item
+ if ((data[index] & 0xFF) == 0x80){
+ return (byte) (data[index] - 0x80);
+ }
+
+ // single byte item
+ if ((data[index] & 0xFF) < 0x80){
+ return (byte) (data[index]);
+ }
+
+ // single byte item
+ if ((data[index] & 0xFF) == 0x81){
+ return (byte) (data[index + 1]);
+ }
+
+
+ return 0;
+ }
+
+ public static String decodeStringItem(byte[] data, int index){
+
+ String value = null;
+
+ if ((data[index] & 0xFF) >= 0xB7 && (data[index] & 0xFF) < 0xC0) {
+
+ byte lenghtOfLenght = (byte) (data[index] - 0xB7);
+ byte pow = (byte) (lenghtOfLenght - 1);
+
+ int length = 0;
+
+ for (int i = 1; i <= lenghtOfLenght; ++i){
+
+ length += data[index+ i] << (8 * pow);
+ pow--;
+ }
+
+ value = new String(data, index + lenghtOfLenght + 1, length);
+
+ }else if ((data[index] & 0xFF) > 0x80 && (data[index] & 0xFF) < 0xB7) {
+
+ byte length = (byte) ((data[index] & 0xFF) - 0x80);
+
+ value = new String(data, index + 1, length);
+
+ } else{
+ throw new Error("wrong decode attempt");
+ }
+
+ return value;
+ }
+
+
+ public static int decodeInt(byte[] data, int index){
+
+ int value = 0;
+
+ if ((data[index] & 0xFF) > 0x80 && (data[index] & 0xFF) < 0xB7) {
+
+ byte length = (byte) (data[index] - 0x80);
+ byte pow = (byte) (length - 1);
+
+
+ for (int i = 1; i <= length; ++i){
+
+ value += data[index + i] << (8 * pow);
+ pow--;
+ }
+ } else{
+
+ throw new Error("wrong decode attempt");
+
+ }
+
+ return value;
+ }
+
+ public static short decodeShort(byte[] data, int index){
+
+ short value = 0;
+
+ if ((data[index] & 0xFF) > 0x80 && (data[index] & 0xFF) < 0xB7) {
+
+ byte length = (byte) (data[index] - 0x80);
+
+ value = ByteBuffer.wrap(data, index, length).getShort();
+
+ } else{
+
+ value = data[index];
+ }
+
+ return value;
+ }
+
+ public static short decodeLong(byte[] data, int index){
+
+ short value = 0;
+
+ if ((data[index] & 0xFF) > 0x80 && (data[index] & 0xFF) < 0xB7) {
+
+ byte length = (byte) (data[index] - 0x80);
+ byte pow = (byte) (length - 1);
+
+
+ for (int i = 1; i <= length; ++i){
+
+ value += data[index + i] << (8 * pow);
+ pow--;
+ }
+ } else{
+
+ throw new Error("wrong decode attempt");
+
+ }
+
+ return value;
+ }
+
+
+ public static byte[] decodeItemBytes(byte[] data, int index){
+
+ byte[] value = null;
+
+ if ((data[index] & 0xFF) >= 0xB7 && (data[index] & 0xFF) < 0xC0) {
+
+ byte lenghtOfLenght = (byte) (data[index] - 0xB7);
+ byte pow = (byte) (lenghtOfLenght - 1);
+
+ int length = 0;
+
+ for (int i = 1; i <= lenghtOfLenght; ++i){
+
+ length += data[index+ i] << (8 * pow);
+ pow--;
+ }
+
+
+ byte[] valueBytes = new byte[length];
+ System.arraycopy(data, index, valueBytes, 0, length);
+
+ value = valueBytes;
+
+ } else if ((data[index] & 0xFF) > 0x80 && (data[index] & 0xFF) < 0xB7) {
+
+ byte length = (byte) (data[index] - 0x80);
+
+ byte[] valueBytes = new byte[length];
+ System.arraycopy(data, index, valueBytes, 0, length);
+
+ value = valueBytes;
+
+ } else {
+
+ throw new Error("wrong decode attempt");
+
+ }
+
+ return value;
+ }
+
+ public static BigInteger decodeBigInteger(byte[] data, int index){
+
+ BigInteger value = null;
+
+ if ((data[index] & 0xFF) >= 0xB7 && (data[index] & 0xFF) < 0xC0) {
+
+ byte lenghtOfLenght = (byte) (data[index] - 0xB7);
+ byte pow = (byte) (lenghtOfLenght - 1);
+
+ int length = 0;
+
+ for (int i = 1; i <= lenghtOfLenght; ++i){
+
+ length += data[index+ i] << (8 * pow);
+ pow--;
+ }
+
+
+ byte[] valueBytes = new byte[length];
+ System.arraycopy(data, index, valueBytes, 0, length);
+
+ value = new BigInteger(valueBytes);
+
+ } else if ((data[index] & 0xFF) > 0x80 && (data[index] & 0xFF) < 0xB7) {
+
+ byte length = (byte) (data[index] - 0x80);
+
+ byte[] valueBytes = new byte[length];
+ System.arraycopy(data, index, valueBytes, 0, length);
+
+ value = new BigInteger(valueBytes);
+
+ } else {
+
+ throw new Error("wrong decode attempt");
+
+ }
+
+ return value;
+ }
+
+ public static byte[] decodeByteArray(byte[] data, int index){
+
+ byte[] value = null;
+
+ if ((data[index] & 0xFF) >= 0xB7 && (data[index] & 0xFF) < 0xC0) {
+
+ byte lenghtOfLenght = (byte) (data[index] - 0xB7);
+ byte pow = (byte) (lenghtOfLenght - 1);
+
+ int length = 0;
+
+ for (int i = 1; i <= lenghtOfLenght; ++i){
+
+ length += data[index+ i] << (8 * pow);
+ pow--;
+ }
+
+
+ byte[] valueBytes = new byte[length];
+ System.arraycopy(data, index, valueBytes, 0, length);
+
+ value = valueBytes;
+
+ } else if ((data[index] & 0xFF) > 0x80 && (data[index] & 0xFF) < 0xB7) {
+
+ byte length = (byte) (data[index] - 0x80);
+
+ byte[] valueBytes = new byte[length];
+ System.arraycopy(data, index, valueBytes, 0, length);
+
+ value = valueBytes;
+
+ } else {
+
+ throw new Error("wrong decode attempt");
+
+ }
+
+ return value;
+ }
+
+
+ public static int nextItemLength(byte[] data, int index){
+
+
+ if (index >= data.length) return -1;
+
+ if ((data[index] & 0xFF) >= 0xF7){
+ byte lenghtOfLenght = (byte) (data[index] - 0xF7);
+ byte pow = (byte) (lenghtOfLenght - 1);
+
+ int length = 0;
+
+ for (int i = 1; i <= lenghtOfLenght; ++i){
+
+ length += (data[index + i] & 0xFF) << (8 * pow);
+ pow--;
+ }
+
+ return length;
+
+
+ }
+
+ if ((data[index] & 0xFF) >= 0xC0 && (data[index] & 0xFF) < 0xF7){
+
+ byte length = (byte) ((data[index] & 0xFF) - 0xC0);
+ return length;
+ }
+
+
+ if ((data[index] & 0xFF) >= 0xB7 && (data[index] & 0xFF) < 0xC0) {
+
+ byte lenghtOfLenght = (byte) (data[index] - 0xB7);
+ byte pow = (byte) (lenghtOfLenght - 1);
+
+ int length = 0;
+
+ for (int i = 1; i <= lenghtOfLenght; ++i){
+
+ length += (data[index + i] & 0xFF) << (8 * pow);
+ pow--;
+ }
+
+ return length ;
+
+
+ }
+
+ if ((data[index] & 0xFF) > 0x80 && (data[index] & 0xFF) < 0xB7) {
+
+ byte length = (byte) ((data[index] & 0xFF) - 0x80);
+ return length;
+ }
+
+
+ if ((data[index] & 0xFF) == 0x80){
+
+ return 1;
+ }
+
+ if ((data[index] & 0xFF) < 0x80) {
+
+ return 1;
+ }
+
+ return -1;
+ }
+
+
+ public static byte[] decodeIP4Bytes(byte[] data, int index){
+
+ int length = (data[index] & 0xFF) - 0xC0;
+ int offset = 1;
+
+ byte aByte = decodeOneByteItem(data, index + offset);
+
+ if ((data[index + offset] & 0xFF) > 0x80) offset = offset + 2; else offset = offset + 1;
+ byte bByte = decodeOneByteItem(data, index + offset);
+
+ if ((data[index + offset] & 0xFF) > 0x80) offset = offset + 2; else offset = offset + 1;
+ byte cByte = decodeOneByteItem(data, index + offset);
+
+ if ((data[index + offset] & 0xFF) > 0x80) offset = offset + 2; else offset = offset + 1;
+ byte dByte = decodeOneByteItem(data, index + offset);
+
+ byte[] ip = new byte[4];
+ ip[0] = aByte;
+ ip[1] = bByte;
+ ip[2] = cByte;
+ ip[3] = dByte;
+
+ return ip;
+ }
+
+ public static int getFirstListElement(byte[] payload, int pos) {
+
+ if (pos >= payload.length) return -1;
+
+ if ((payload[pos] & 0xFF) >= 0xF7){
+
+ byte lenghtOfLenght = (byte) (payload[pos] - 0xF7);
+
+ return pos + lenghtOfLenght + 1 ;
+ }
+
+ if ((payload[pos] & 0xFF) >= 0xC0 && (payload[pos] & 0xFF) < 0xF7){
+
+ byte length = (byte) ((payload[pos] & 0xFF) - 0xC0);
+ return pos + 1;
+ }
+
+
+ if ((payload[pos] & 0xFF) >= 0xB7 && (payload[pos] & 0xFF) < 0xC0) {
+
+ byte lenghtOfLenght = (byte) (payload[pos] - 0xB7);
+ byte pow = (byte) (lenghtOfLenght - 1);
+
+ int length = 0;
+
+ for (int i = 1; i <= lenghtOfLenght; ++i){
+
+ length += (payload[pos + i] & 0xFF) << (8 * pow);
+ pow--;
+ }
+
+ return pos + lenghtOfLenght + 1 ;
+ }
+
+ return -1;
+ }
+
+
+
+
+
+
+ public static int getNextElementIndex(byte[] payload, int pos) {
+
+ if (pos >= payload.length) return -1;
+
+ if ((payload[pos] & 0xFF) >= 0xF7){
+ byte lenghtOfLenght = (byte) (payload[pos] - 0xF7);
+ byte pow = (byte) (lenghtOfLenght - 1);
+
+ int length = 0;
+
+ for (int i = 1; i <= lenghtOfLenght; ++i){
+
+ length += (payload[pos + i] & 0xFF) << (8 * pow);
+ pow--;
+ }
+
+ return pos + lenghtOfLenght + length + 1 ;
+
+
+ }
+
+ if ((payload[pos] & 0xFF) >= 0xC0 && (payload[pos] & 0xFF) < 0xF7){
+
+ byte length = (byte) ((payload[pos] & 0xFF) - 0xC0);
+ return pos + 1 + length;
+ }
+
+
+ if ((payload[pos] & 0xFF) >= 0xB7 && (payload[pos] & 0xFF) < 0xC0) {
+
+ byte lenghtOfLenght = (byte) (payload[pos] - 0xB7);
+ byte pow = (byte) (lenghtOfLenght - 1);
+
+ int length = 0;
+
+ for (int i = 1; i <= lenghtOfLenght; ++i){
+
+ length += (payload[pos + i] & 0xFF) << (8 * pow);
+ pow--;
+ }
+
+ return pos + lenghtOfLenght + length + 1 ;
+ }
+
+ if ((payload[pos] & 0xFF) > 0x80 && (payload[pos] & 0xFF) < 0xB7) {
+
+ byte length = (byte) ((payload[pos] & 0xFF) - 0x80);
+ return pos + 1 + length;
+ }
+
+
+ if ((payload[pos] & 0xFF) == 0x80){
+
+ return pos + 1;
+ }
+
+ if ((payload[pos] & 0xFF) < 0x80) {
+
+ return pos + 1;
+ }
+
+
+
+ return -1;
+ }
+
+
+ /**
+ * Get exactly one message payload
+ */
+ public static void fullTraverse(byte [] msgData, int level, int startPos, int endPos,
+ int levelToIndex, Queue index) {
+
+ try{
+
+ if (msgData == null || msgData.length == 0) return ;
+ int pos = startPos;
+
+ while(pos < endPos){
+
+ if (level == levelToIndex) index.add(new Integer(pos));
+
+ // It's a list with a payload more than 55 bytes
+ // data[0] - 0xF7 = how many next bytes allocated
+ // for the length of the list
+ if ((msgData[pos] & 0xFF) >= 0xF7){
+
+ byte lenghtOfLenght = (byte) (msgData[pos] - 0xF7);
+ byte pow = (byte) (lenghtOfLenght - 1);
+
+ int length = 0;
+
+ for (int i = 1; i <= lenghtOfLenght; ++i){
+
+ length += (msgData[pos + i] & 0xFF) << (8 * pow);
+ pow--;
+ }
+
+ // now we can parse an item for data[1]..data[length]
+ System.out.println("-- level: [" + level + "] Found big list length: " + length);
+
+ fullTraverse(msgData, level + 1, pos + lenghtOfLenght + 1, pos + lenghtOfLenght + length,
+ levelToIndex, index);
+
+ pos += lenghtOfLenght + length + 1 ;
+ continue;
+ }
+
+
+ // It's a list with a payload less than 55 bytes
+ if ((msgData[pos] & 0xFF) >= 0xC0 && (msgData[pos] & 0xFF) < 0xF7){
+
+ byte length = (byte) ((msgData[pos] & 0xFF) - 0xC0);
+
+ System.out.println("-- level: [" + level + "] Found small list length: " + length);
+
+ fullTraverse(msgData, level + 1, pos + 1, pos + length + 1, levelToIndex, index);
+
+ pos += 1 + length;
+ continue;
+ }
+
+
+ // It's an item with a payload more than 55 bytes
+ // data[0] - 0xB7 = how much next bytes allocated for
+ // the length of the string
+ if ((msgData[pos] & 0xFF) >= 0xB7 && (msgData[pos] & 0xFF) < 0xC0) {
+
+ byte lenghtOfLenght = (byte) (msgData[pos] - 0xB7);
+ byte pow = (byte) (lenghtOfLenght - 1);
+
+ int length = 0;
+
+ for (int i = 1; i <= lenghtOfLenght; ++i){
+
+ length += (msgData[pos + i] & 0xFF) << (8 * pow);
+ pow--;
+ }
+
+
+ // now we can parse an item for data[1]..data[length]
+ System.out.println("-- level: [" + level + "] Found big item length: " + length);
+ pos += lenghtOfLenght + length + 1 ;
+
+ continue;
+ }
+
+
+ // It's an item less than 55 bytes long,
+ // data[0] - 0x80 == lenght of the item
+ if ((msgData[pos] & 0xFF) > 0x80 && (msgData[pos] & 0xFF) < 0xB7) {
+
+ byte length = (byte) ((msgData[pos] & 0xFF) - 0x80);
+
+ System.out.println("-- level: [" + level + "] Found small item length: " + length);
+ pos += 1 + length;
+
+ continue;
+ }
+
+ // null item
+ if ((msgData[pos] & 0xFF) == 0x80){
+ System.out.println("-- level: [" + level + "] Found null item: ");
+ pos += 1;
+ continue;
+ }
+
+ // single byte item
+ if ((msgData[pos] & 0xFF) < 0x80) {
+ System.out.println("-- level: [" + level + "] Found single item: ");
+ pos += 1;
+ continue;
+ }
+ }
+
+
+ } catch(Throwable th){
+
+ throw new Error("wire packet not parsed correctly", th.fillInStackTrace());
+
+ }
+
+
+
+ }
+
+
+
+
+
+ /**
+ * Parse wire byte[] message into RLP elements
+ *
+ * @param msgData - raw RLP data
+ * @param rlpList - outcome of recursive RLP structure
+ */
+ public static void parseObjects(byte [] msgData, RLPList rlpList){
+
+ RLP.fullTraverse2(msgData, 0, 0, msgData.length, 1, rlpList);
+ }
+
+ /**
+ * Get exactly one message payload
+ */
+ public static void fullTraverse2(byte [] msgData, int level, int startPos, int endPos,
+ int levelToIndex, RLPList rlpList) {
+
+ try{
+
+ if (msgData == null || msgData.length == 0) return ;
+ int pos = startPos;
+
+ while(pos < endPos){
+
+ // It's a list with a payload more than 55 bytes
+ // data[0] - 0xF7 = how many next bytes allocated
+ // for the length of the list
+ if ((msgData[pos] & 0xFF) >= 0xF7){
+
+ byte lenghtOfLenght = (byte) (msgData[pos] - 0xF7);
+ byte pow = (byte) (lenghtOfLenght - 1);
+
+ int length = 0;
+
+ for (int i = 1; i <= lenghtOfLenght; ++i){
+
+ length += (msgData[pos + i] & 0xFF) << (8 * pow);
+ pow--;
+ }
+
+
+ byte[] rlpData = new byte[lenghtOfLenght + length + 1];
+ System.arraycopy(msgData, pos, rlpData, 0, lenghtOfLenght + length + 1);
+
+ RLPList newLevelList = new RLPList();
+ newLevelList.setRLPData(rlpData);
+
+
+// todo: this done to get some data for testing should be delete
+// byte[] subList = Arrays.copyOfRange(msgData, pos, pos + lenghtOfLenght + length + 1);
+// System.out.println(Utils.toHexString(subList));
+
+
+ fullTraverse2(msgData, level + 1, pos + lenghtOfLenght + 1, pos + lenghtOfLenght + length + 1,
+ levelToIndex, newLevelList);
+ rlpList.addItem(newLevelList);
+
+ pos += lenghtOfLenght + length + 1;
+ continue;
+ }
+
+
+ // It's a list with a payload less than 55 bytes
+ if ((msgData[pos] & 0xFF) >= 0xC0 && (msgData[pos] & 0xFF) < 0xF7){
+
+ byte length = (byte) ((msgData[pos] & 0xFF) - 0xC0);
+
+ byte[] rlpData = new byte[length + 1];
+ System.arraycopy(msgData, pos, rlpData, 0, length + 1);
+
+ RLPList newLevelList = new RLPList();
+ newLevelList.setRLPData(rlpData);
+
+ if (length > 0)
+ fullTraverse2(msgData, level + 1, pos + 1, pos + length + 1, levelToIndex, newLevelList);
+ rlpList.addItem(newLevelList);
+
+ pos += 1 + length;
+ continue;
+ }
+
+
+ // It's an item with a payload more than 55 bytes
+ // data[0] - 0xB7 = how much next bytes allocated for
+ // the length of the string
+ if ((msgData[pos] & 0xFF) >= 0xB7 && (msgData[pos] & 0xFF) < 0xC0) {
+
+ byte lenghtOfLenght = (byte) (msgData[pos] - 0xB7);
+ byte pow = (byte) (lenghtOfLenght - 1);
+
+ int length = 0;
+
+ for (int i = 1; i <= lenghtOfLenght; ++i){
+
+ length += (msgData[pos + i] & 0xFF) << (8 * pow);
+ pow--;
+ }
+
+
+ // now we can parse an item for data[1]..data[length]
+ byte[] item = new byte[length];
+ System.arraycopy(msgData, pos + lenghtOfLenght + 1, item, 0, length);
+
+ byte[] rlpPrefix = new byte[lenghtOfLenght + 1];
+ System.arraycopy(msgData, pos, rlpPrefix, 0, lenghtOfLenght + 1);
+
+ RLPItem rlpItem= new RLPItem(item);
+ rlpList.addItem(rlpItem);
+ pos += lenghtOfLenght + length + 1 ;
+
+
+ continue;
+ }
+
+
+ // It's an item less than 55 bytes long,
+ // data[0] - 0x80 == length of the item
+ if ((msgData[pos] & 0xFF) > 0x80 && (msgData[pos] & 0xFF) < 0xB7) {
+
+ byte length = (byte) ((msgData[pos] & 0xFF) - 0x80);
+
+
+ byte[] item = new byte[length];
+ System.arraycopy(msgData, pos + 1, item, 0, length);
+
+ byte[] rlpPrefix = new byte[2];
+ System.arraycopy(msgData, pos, rlpPrefix, 0, 2);
+
+ RLPItem rlpItem= new RLPItem(item);
+ rlpList.addItem(rlpItem);
+ pos += 1 + length;
+
+ continue;
+ }
+
+ // null item
+ if ((msgData[pos] & 0xFF) == 0x80){
+
+ byte[] item = new byte[0];
+ RLPItem rlpItem= new RLPItem(item);
+ rlpList.addItem(rlpItem);
+ pos += 1;
+ continue;
+ }
+
+ // single byte item
+ if ((msgData[pos] & 0xFF) < 0x80) {
+
+ byte[] item = {(byte)(msgData[pos] & 0xFF)};
+
+ RLPItem rlpItem= new RLPItem(item);
+ rlpList.addItem(rlpItem);
+ pos += 1;
+ continue;
+ }
+ }
+
+
+ } catch(Throwable th){
+
+ throw new Error("wire packet not parsed correctly", th.fillInStackTrace());
+
+ }
+
+ }
+
+
+
+/*
+ def rlp_encode(input):
+ if isinstance(input,str):
+ if len(input) == 1 and chr(input) < 128: return input
+ else: return encode_length(len(input),128) + input
+ elif isinstance(input,list):
+ output = ''
+ for item in input: output += rlp_encode(item)
+ return encode_length(len(output),192) + output
+
+def encode_length(L,offset):
+ if L < 56:
+ return chr(L + offset)
+ elif L < 256**8:
+ BL = to_binary(L)
+ return chr(len(BL) + offset + 55) + BL
+ else:
+ raise Exception("input too long")
+
+ def to_binary(x):
+ return '' if x == 0 else to_binary(int(x / 256)) + chr(x % 256)
+*/
+
+ private static void test1(Object... item){
+
+ }
+
+ private static String rlpEncode(Object item){
+
+ if (item instanceof String){
+
+ String str = ((String)item);
+ int length = str.length();
+ if (length == 1 && str.charAt(0) < 128 ) return str;
+ else return encodeLenght(str.length(), 128) + str;
+ } else if(item instanceof List) {
+
+ List itemList = (List) item;
+ StringBuilder output = new StringBuilder();
+
+ for (Object oneItem : itemList) output.append(rlpEncode(oneItem));
+ return encodeLenght(output.toString().length(), 192) + output.toString();
+ }
+
+ throw new Error("unsupported type" + item.getClass());
+ }
+
+
+
+ private static String encodeLenght(int L, int offset) {
+
+ if (L < 56)
+ return "" + (char) (L + offset);
+ else if
+ (L < (256 ^ 8)) {
+
+ String BL = toBinary(L);
+ return "" + (char) (BL.length() + offset + 55) + BL;
+ }
+ else
+ throw new Error("input too long");
+
+ }
+
+
+ public static byte getCommandCode(byte[] data){
+
+ byte command = 0;
+
+ int index = getFirstListElement(data, 0);
+
+ command = data[index];
+
+ command = ((int)(command & 0xFF) == 0x80) ? 0 : command;
+
+
+ return command;
+ }
+
+
+ public static Object decode(char[] data){
+
+ if (data == null || data.length == 0) return null;
+
+
+
+
+
+ if (data[0] >= 0xF7){ /* It's a list with a payload more than 55 bytes
+ data[0] - 0xF7 = how many next bytes allocated
+ for the length of the list */;
+
+
+ byte lenghtOfLenght = (byte) (data[0] - 0xF7);
+ byte pow = (byte) (lenghtOfLenght - 1);
+
+ long length = 0;
+
+ for (int i = 1; i <= lenghtOfLenght; ++i){
+
+ length += data[i] << (8 * pow);
+ pow--;
+ }
+
+ System.out.println(length);
+
+
+ // now we can parse an item for data[1]..data[length]
+ }
+
+ if (data[0] >= 0xC0 && data[0] < 0xF7) /* It's a list with a payload less than 55 bytes*/;
+
+
+
+ if (data[0] >= 0xB7 && data[0] < 0xC0) {/* It's an item with a payload more than 55 bytes
+ data[0] - 0xB7 = how much next bytes allocated for
+ the length of the string */;
+
+ byte lenghtOfLenght = (byte) (data[0] - 0xB7);
+ byte pow = (byte) (lenghtOfLenght - 1);
+
+ long length = 0;
+
+ for (int i = 1; i <= lenghtOfLenght; ++i){
+
+ length += data[i] << (8 * pow);
+ pow--;
+ }
+
+
+ // now we can parse an item for data[1]..data[length]
+
+ }
+
+
+ if (data[0] >= 0x80 && data[0] < 0xB7) {/* It's an item less than 55 bytes long,
+ data[0] - 0x80 == lenght of the item */;
+
+ }
+
+ if (data[0] == 0x80) /* null item */;
+ if (data[0] < 0x80) /* single byte item */;
+
+
+
+ return null;
+ }
+
+
+ private static String toBinary(int x){
+
+ if (x == 0) return "";
+ else return toBinary(x >> 8) + ((char)(x & 0x00FF));
+
+ }
+
+
+
+ public static byte[] encodeByte(byte singleByte){
+
+ if ((singleByte & 0xFF) == 0){
+
+ return new byte[]{(byte) 0x80};
+ } else if ((singleByte & 0xFF) < 0x7F){
+
+ return new byte[]{singleByte};
+ } else {
+
+ return new byte[]{(byte) 0x81, singleByte};
+ }
+ }
+
+ public static byte[] encodeShort(short singleShort){
+
+ if (singleShort <= 0xFF)
+
+ return encodeByte((byte)singleShort);
+ else{
+
+ return new byte[]{(byte) 0x82,
+ (byte) (singleShort >> 8 & 0xFF),
+ (byte) (singleShort >> 0 & 0xFF)
+ };
+
+ }
+ }
+
+ public static byte[] encodeString(String srcString){
+
+ return encodeElement(srcString.getBytes());
+ }
+
+ public static byte[] encodeBigInteger(BigInteger srcBigInteger){
+
+ return encodeElement(srcBigInteger.toByteArray());
+ }
+
+ public static byte[] encodeElement(byte[] srcData){
+
+ if (srcData.length <= 0x37){
+
+ // length = 8X
+ byte length = (byte) (0x80 + srcData.length);
+
+ byte[] data = Arrays.copyOf(srcData, srcData.length + 1);
+ System.arraycopy(data, 0, data, 1, srcData.length);
+ data[0] = length;
+
+ return data;
+
+ } else {
+
+ // length of length = BX
+ // prefix = [BX, [length]]
+ int tmpLenght = srcData.length;
+ byte byteNum = 0;
+ while (tmpLenght != 0){
+
+ ++byteNum;
+ tmpLenght = tmpLenght >> 8;
+ }
+
+ byte[] lenBytes = new byte[byteNum];
+ for (int i = 0; i < byteNum; ++i){
+ lenBytes[0] = (byte)((srcData.length >> (8 * i)) & 0xFF);
+ }
+
+ // first byte = F7 + bytes.length
+ byte[] data = Arrays.copyOf(srcData, srcData.length + 1 + byteNum);
+ System.arraycopy(data, 0, data, 1 + byteNum, srcData.length);
+ data[0] = (byte)(0xB7 + byteNum);
+ System.arraycopy(lenBytes, 0, data, 1, lenBytes.length);
+
+ return data;
+ }
+
+ }
+
+
+ public static byte[] encodeList(byte[]... elements){
+
+ int totalLength = 0;
+ for (int i = 0; i < elements.length; ++i){
+
+ totalLength += elements[i].length;
+ }
+
+ byte[] data;
+ int copyPos = 0;
+ if (totalLength <= 0x37){
+
+ data = new byte[1 + totalLength];
+ data[0] = (byte)(0xC0 + totalLength);
+ copyPos = 1;
+
+ } else {
+
+ // length of length = BX
+ // prefix = [BX, [length]]
+ int tmpLenght = totalLength;
+ byte byteNum = 0;
+ while (tmpLenght != 0){
+
+ ++byteNum;
+ tmpLenght = tmpLenght >> 8;
+ }
+
+ tmpLenght = totalLength;
+ byte[] lenBytes = new byte[byteNum];
+ for (int i = 0; i < byteNum; ++i){
+ lenBytes[i] = (byte)((tmpLenght >> (8 * i)) & 0xFF);
+ }
+
+ // first byte = F7 + bytes.length
+ data = new byte[1 + lenBytes.length + totalLength];
+ data[0] = (byte)(0xF7 + byteNum);
+ System.arraycopy(lenBytes, 0, data, 1, lenBytes.length);
+
+ copyPos = lenBytes.length + 1;
+ }
+
+
+ for (int i = 0; i < elements.length; ++i){
+
+ System.arraycopy(elements[i], 0, data, copyPos, elements[i].length);
+ copyPos += elements[i].length;
+ }
+
+ return data;
+ }
+
+
+
+
+
+ public static void main(String args[]){
+
+ char[] data = { 0xF9, 20, 100 };
+
+ decode(data);
+
+
+
+
+ }
+
+
+ public static void main2(String args[]) throws UnsupportedEncodingException {
+
+
+ List